From 2e014fa952fe8fe9668cdb67f64d32f813407449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Choutri?= Date: Fri, 27 Dec 2024 15:17:06 +0100 Subject: [PATCH] Adapt advisories listing for search Search results will also show qualified package names associated with an advisory --- app/cli/DesignSystem.hs | 12 +++++++++++- assets/css/2-components/11-advisory-list-item.css | 13 +++++++++---- src/advisories/Advisories/Model/Affected/Query.hs | 9 +++++++++ src/advisories/Advisories/Model/Affected/Types.hs | 2 ++ src/web/FloraWeb/Components/AdvisoryListItem.hs | 14 ++++++++++---- src/web/FloraWeb/Pages/Templates/Packages.hs | 12 +++++++----- src/web/FloraWeb/Pages/Templates/Screens/Search.hs | 4 ++-- 7 files changed, 50 insertions(+), 16 deletions(-) diff --git a/app/cli/DesignSystem.hs b/app/cli/DesignSystem.hs index 83f61714..304f88c6 100644 --- a/app/cli/DesignSystem.hs +++ b/app/cli/DesignSystem.hs @@ -152,6 +152,8 @@ packageAdvisoriesExample = do Vector.fromList [ PackageAdvisoryPreview { hsecId = fromJust $ HsecId.parseHsecId "HSEC-2023-0009" + , namespace = Namespace "hackage" + , packageName = PackageName "git-annex" , summary = "git-annex command injection via malicious SSH hostname" , fixed = True , published = read "2023-07-25 13:25:42 UTC" @@ -159,6 +161,8 @@ packageAdvisoriesExample = do } , PackageAdvisoryPreview { hsecId = fromJust $ HsecId.parseHsecId "HSEC-2023-0010" + , namespace = Namespace "hackage" + , packageName = PackageName "git-annex" , summary = "git-annex private data exfiltration to compromised remote" , fixed = True , published = read "2023-07-25 13:25:42 UTC" @@ -166,6 +170,8 @@ packageAdvisoriesExample = do } , PackageAdvisoryPreview { hsecId = fromJust $ HsecId.parseHsecId "HSEC-2023-0012" + , namespace = Namespace "hackage" + , packageName = PackageName "git-annex" , summary = "git-annex checksum exposure to encrypted special remotes" , fixed = True , published = read "2023-07-25 13:25:42 UTC" @@ -173,6 +179,8 @@ packageAdvisoriesExample = do } , PackageAdvisoryPreview { hsecId = fromJust $ HsecId.parseHsecId "HSEC-2023-0013" + , namespace = Namespace "hackage" + , packageName = PackageName "git-annex" , summary = "git-annex plaintext storage of embedded credentials on encrypted remotes" , fixed = True , published = read "2023-07-25 13:25:42 UTC" @@ -180,10 +188,12 @@ packageAdvisoriesExample = do } , PackageAdvisoryPreview { hsecId = fromJust $ HsecId.parseHsecId "HSEC-2023-0011" + , namespace = Namespace "hackage" + , packageName = PackageName "git-annex" , summary = "git-annex GPG decryption attack via compromised remote" , fixed = True , published = read "2023-07-25 13:25:42 UTC" , cvss = fromRight' $ parseCVSS "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N" } ] - ul_ [class_ "advisory-list"] $ Vector.forM_ advisoryPreviews (\preview -> Component.advisoryListRow preview) + ul_ [class_ "advisory-list"] $ Vector.forM_ advisoryPreviews (\preview -> Component.advisoryListRow True preview) diff --git a/assets/css/2-components/11-advisory-list-item.css b/assets/css/2-components/11-advisory-list-item.css index 128f9565..70095f2d 100644 --- a/assets/css/2-components/11-advisory-list-item.css +++ b/assets/css/2-components/11-advisory-list-item.css @@ -9,22 +9,27 @@ display: inline; } -.package-advisory-list-item__hsec_id { +.package-advisory-list-item__package { + font-weight: bolder; order: 1; } -.package-advisory-list-item__published { +.package-advisory-list-item__hsec-id { order: 2; +} + +.package-advisory-list-item__published { + order: 3; display: none; } .package-advisory-list-item__attributes { - order: 3; + order: 4; } .package-advisory-list-item__summary { font-weight: bolder; - order: 4; + order: 5; } .advisory-list-item__severity-pill { diff --git a/src/advisories/Advisories/Model/Affected/Query.hs b/src/advisories/Advisories/Model/Affected/Query.hs index 88df575c..d92d1378 100644 --- a/src/advisories/Advisories/Model/Affected/Query.hs +++ b/src/advisories/Advisories/Model/Affected/Query.hs @@ -49,6 +49,8 @@ getAdvisoryPreviewsByPackageId packageId = Select [sql| SELECT s0.hsec_id + , p3.namespace + , p3.name , s0.summary , CASE WHEN a2.fixed_version IS NULL @@ -78,6 +80,8 @@ searchAdvisoriesQuery = [sql| WITH results AS ( SELECT s0.hsec_id + , p3.namespace + , p3.name , s0.summary , CASE WHEN a2.fixed_version IS NULL @@ -92,12 +96,15 @@ WITH results AS ( INNER JOIN affected_version_ranges AS a2 ON a1.affected_package_id = a2.affected_package_id INNER JOIN packages AS p3 ON a1.package_id = p3.package_id WHERE ? <% s0.summary + GROUP BY s0.hsec_id, p3.namespace, p3.name, s0.summary, fixed, s0.published, a1.cvss, rating ORDER BY rating desc, s0.summary asc OFFSET ? LIMIT ? ) SELECT r0.hsec_id + , r0.namespace + , r0.name , r0.summary , r0.fixed , r0.published @@ -122,6 +129,8 @@ countAdvisorySearchResultsQuery = [sql| WITH results AS ( SELECT s0.hsec_id + , p3.namespace + , p3.name , s0.summary , CASE WHEN a2.fixed_version IS NULL diff --git a/src/advisories/Advisories/Model/Affected/Types.hs b/src/advisories/Advisories/Model/Affected/Types.hs index d8dbfffe..e061151f 100644 --- a/src/advisories/Advisories/Model/Affected/Types.hs +++ b/src/advisories/Advisories/Model/Affected/Types.hs @@ -78,6 +78,8 @@ data AffectedVersionRangeDAO = AffectedVersionRangeDAO data PackageAdvisoryPreview = PackageAdvisoryPreview { hsecId :: HsecId + , namespace :: Namespace + , packageName :: PackageName , summary :: Text , fixed :: Bool , published :: UTCTime diff --git a/src/web/FloraWeb/Components/AdvisoryListItem.hs b/src/web/FloraWeb/Components/AdvisoryListItem.hs index f96f828f..2d8cc973 100644 --- a/src/web/FloraWeb/Components/AdvisoryListItem.hs +++ b/src/web/FloraWeb/Components/AdvisoryListItem.hs @@ -2,6 +2,7 @@ module FloraWeb.Components.AdvisoryListItem ( advisoryListRow ) where +import Control.Monad (when) import Data.Text.Display import Data.Time qualified as Time import Lucid @@ -9,15 +10,16 @@ import Security.CVSS (Rating (..), cvssScore) import Advisories.HsecId.Orphans () import Advisories.Model.Affected.Types -import Control.Monad (when) import Distribution.Orphans.Version () import FloraWeb.Components.Pill +import FloraWeb.Links qualified as Links import FloraWeb.Pages.Templates.Types advisoryListRow - :: PackageAdvisoryPreview + :: Bool + -> PackageAdvisoryPreview -> FloraHTML -advisoryListRow preview = do +advisoryListRow specifyPackage preview = do let href = "https://haskell.github.io/security-advisories/advisory/" <> display preview.hsecId <> ".html" let (rating, score) = cvssScore preview.cvss let severity = case rating of @@ -27,11 +29,15 @@ advisoryListRow preview = do High -> ratingHigh score Critical -> ratingCritical score div_ [class_ "package-advisory-list-item"] $ do - div_ [class_ "package-advisory-list-item__hsec-id md:order-1"] $ do + div_ [class_ "package-advisory-list-item__hsec-id"] $ do a_ [href_ href] (toHtml $ display preview.hsecId) span_ [class_ "package-advisory-list-item__inline-published"] $ toHtml $ Time.formatTime Time.defaultTimeLocale "%_d %b %Y" preview.published + when specifyPackage $ + div_ [class_ "package-advisory-list-item__package"] $ do + let qualifiedName = toHtml $ display preview.namespace <> "/" <> display preview.packageName + a_ [class_ "", href_ $ Links.packageResource preview.namespace preview.packageName] qualifiedName div_ [class_ "package-advisory-list-item__summary"] (toHtml preview.summary) div_ [class_ "package-advisory-list-item__published"] $ toHtml $ diff --git a/src/web/FloraWeb/Pages/Templates/Packages.hs b/src/web/FloraWeb/Pages/Templates/Packages.hs index 89af92c1..b80036e8 100644 --- a/src/web/FloraWeb/Pages/Templates/Packages.hs +++ b/src/web/FloraWeb/Pages/Templates/Packages.hs @@ -24,7 +24,7 @@ module FloraWeb.Pages.Templates.Packages , showDependencies , showDependents , showPackageSecurityPage - , advisoriesListing + , packageAdvisoriesListing ) where import Control.Monad (when) @@ -567,17 +567,19 @@ showPackageSecurityPage showPackageSecurityPage namespace packageName advisoryPreviews = do div_ [class_ "container"] $ do presentationHeaderForAdvisories namespace packageName - advisoriesListing advisoryPreviews + packageAdvisoriesListing False advisoryPreviews -advisoriesListing :: Vector PackageAdvisoryPreview -> FloraHTML -advisoriesListing advisoryPreviews = +packageAdvisoriesListing :: Bool -> Vector PackageAdvisoryPreview -> FloraHTML +packageAdvisoriesListing specifyPackage advisoryPreviews = if Vector.null advisoryPreviews then p_ [] "No advisories found." else div_ [class_ "advisory-list"] $ do div_ [class_ "advisory-list__head"] $ do div_ [class_ "advisory-list__header"] "ID" + when specifyPackage $ + div_ [class_ "advisory-list__header"] "Package" div_ [class_ "advisory-list__header"] "Summary" div_ [class_ "advisory-list__header"] "Published" div_ [class_ "advisory-list__header"] "Attributes" div_ [class_ "advisory-list__body"] $ - Vector.forM_ advisoryPreviews (\preview -> advisoryListRow preview) + Vector.forM_ advisoryPreviews (\preview -> advisoryListRow specifyPackage preview) diff --git a/src/web/FloraWeb/Pages/Templates/Screens/Search.hs b/src/web/FloraWeb/Pages/Templates/Screens/Search.hs index 04095bdc..70da2979 100644 --- a/src/web/FloraWeb/Pages/Templates/Screens/Search.hs +++ b/src/web/FloraWeb/Pages/Templates/Screens/Search.hs @@ -13,7 +13,7 @@ import Flora.Search (SearchAction (..)) import FloraWeb.Components.PackageListHeader (presentationHeader) import FloraWeb.Components.PaginationNav (paginationNav) import FloraWeb.Pages.Templates -import FloraWeb.Pages.Templates.Packages (advisoriesListing, packageListing, packageWithExecutableListing) +import FloraWeb.Pages.Templates.Packages (packageAdvisoriesListing, packageListing, packageWithExecutableListing) showAllPackages :: Word -> Positive Word -> Vector PackageInfo -> FloraHTML showAllPackages count currentPage packagesInfo = do @@ -75,6 +75,6 @@ showAdvisorySearchResults showAdvisorySearchResults searchTerm count currentPage results = do div_ [class_ "container"] $ do presentationHeader searchTerm "" count - advisoriesListing results + packageAdvisoriesListing True results when (count > 30) $ paginationNav count currentPage (SearchInAdvisories searchTerm)