From 2b1ba8d668620dd1e61c737a542e5c96acba2a44 Mon Sep 17 00:00:00 2001 From: Tristan Cacqueray Date: Thu, 27 Jun 2024 15:48:06 -0400 Subject: [PATCH 1/7] Add Advisory.Ecosystem to support GHC's advisory This change updates the affected schema to support GHC ecosystem with the "ghc-component" key. This change also implements a new OOB attribute to validate that the advisory path matchs at least one affected. --- .../src/Security/Advisories/Core/Advisory.hs | 28 +++++++++++++-- code/hsec-tools/app/Main.hs | 14 ++++++-- .../src/Security/Advisories/Convert/OSV.hs | 14 +++++--- .../src/Security/Advisories/Filesystem.hs | 23 +++++++++--- .../src/Security/Advisories/Generate/HTML.hs | 10 ++++-- .../src/Security/Advisories/Parse.hs | 36 +++++++++++-------- .../src/Security/Advisories/Queries.hs | 7 ++-- code/hsec-tools/test/Spec.hs | 3 +- code/hsec-tools/test/Spec/QueriesSpec.hs | 2 +- .../test/golden/EXAMPLE_ADVISORY.md.golden | 2 +- 10 files changed, 102 insertions(+), 37 deletions(-) diff --git a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs index 08d30707..cf9d6aa6 100644 --- a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs +++ b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE DerivingVia, OverloadedStrings #-} module Security.Advisories.Core.Advisory ( Advisory(..) @@ -10,6 +10,10 @@ module Security.Advisories.Core.Advisory , AffectedVersionRange(..) , OS(..) , Keyword(..) + , Ecosystem(..) + , GHCComponent(..) + , ghcComponentToText + , ghcComponentFromText ) where @@ -44,10 +48,30 @@ data Advisory = Advisory } deriving stock (Show) +data Ecosystem = Hackage Text | GHC GHCComponent + deriving stock (Show, Eq) + +-- Keep this list in sync with the 'ghcComponentFromText' below +data GHCComponent = GHCCompiler | GHCi | GHCRTS + deriving stock (Show, Eq) + +ghcComponentToText :: GHCComponent -> Text +ghcComponentToText c = case c of + GHCCompiler -> "compiler" + GHCi -> "ghci" + GHCRTS -> "rts" + +ghcComponentFromText :: Text -> Maybe GHCComponent +ghcComponentFromText c = case c of + "compiler" -> Just GHCCompiler + "ghci" -> Just GHCi + "rts" -> Just GHCRTS + _ -> Nothing + -- | An affected package (or package component). An 'Advisory' must -- mention one or more packages. data Affected = Affected - { affectedPackage :: Text + { affectedEcosystem :: Ecosystem , affectedCVSS :: CVSS.CVSS , affectedVersions :: [AffectedVersionRange] , affectedArchitectures :: Maybe [Architecture] diff --git a/code/hsec-tools/app/Main.hs b/code/hsec-tools/app/Main.hs index d68c716f..ac80133c 100644 --- a/code/hsec-tools/app/Main.hs +++ b/code/hsec-tools/app/Main.hs @@ -23,11 +23,18 @@ import Security.Advisories.Generate.HTML import Security.Advisories.Generate.Snapshot import Security.Advisories.Git import Security.Advisories.Queries (listVersionRangeAffectedBy) + import System.Exit (die, exitFailure, exitSuccess) import System.FilePath (takeBaseName) import System.IO (hPrint, hPutStrLn, stderr) import Validation (Validation (..)) +import Security.Advisories.Generate.HTML +import Security.Advisories.Filesystem (parseEcosystem) + +import qualified Command.Reserve + + main :: IO () main = join $ @@ -167,11 +174,14 @@ withAdvisory go file = do oob <- runExceptT $ case file of Nothing -> throwE StdInHasNoOOB - Just path -> withExceptT GitHasNoOOB $ do - gitInfo <- ExceptT $ liftIO $ getAdvisoryGitInfo path + Just path -> do + ecosystem <- parseEcosystem path + withExceptT GitHasNoOOB $ do + gitInfo <- ExceptT $ liftIO $ getAdvisoryGitInfo path pure OutOfBandAttributes { oobPublished = firstAppearanceCommitDate gitInfo , oobModified = lastModificationCommitDate gitInfo + , oobEcosystem = ecosystem } case parseAdvisory NoOverrides oob input of diff --git a/code/hsec-tools/src/Security/Advisories/Convert/OSV.hs b/code/hsec-tools/src/Security/Advisories/Convert/OSV.hs index 07cffcfa..10be9484 100644 --- a/code/hsec-tools/src/Security/Advisories/Convert/OSV.hs +++ b/code/hsec-tools/src/Security/Advisories/Convert/OSV.hs @@ -30,19 +30,23 @@ convert adv = mkAffected :: Affected -> OSV.Affected Void Void Void mkAffected aff = OSV.Affected - { OSV.affectedPackage = mkPackage (affectedPackage aff) + { OSV.affectedPackage = mkPackage (affectedEcosystem aff) , OSV.affectedRanges = pure $ mkRange (affectedVersions aff) , OSV.affectedSeverity = [OSV.Severity (affectedCVSS aff)] , OSV.affectedEcosystemSpecific = Nothing , OSV.affectedDatabaseSpecific = Nothing } -mkPackage :: T.Text -> OSV.Package -mkPackage name = OSV.Package - { OSV.packageName = name - , OSV.packageEcosystem = "Hackage" +mkPackage :: Ecosystem -> OSV.Package +mkPackage ecosystem = OSV.Package + { OSV.packageName = packageName + , OSV.packageEcosystem = ecosystemName , OSV.packagePurl = Nothing } + where + (ecosystemName, packageName) = case ecosystem of + Hackage n -> ("Hackage", n) + GHC c -> ("GHC", ghcComponentToText c) mkRange :: [AffectedVersionRange] -> OSV.Range Void mkRange ranges = diff --git a/code/hsec-tools/src/Security/Advisories/Filesystem.hs b/code/hsec-tools/src/Security/Advisories/Filesystem.hs index 2e65dc15..3f7a28e5 100644 --- a/code/hsec-tools/src/Security/Advisories/Filesystem.hs +++ b/code/hsec-tools/src/Security/Advisories/Filesystem.hs @@ -21,6 +21,7 @@ module Security.Advisories.Filesystem , forAdvisory , listAdvisories , advisoryFromFile + , parseEcosystem ) where import Control.Applicative (liftA2) @@ -31,17 +32,19 @@ import Data.Traversable (for) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Writer.Strict (execWriterT, tell) +import qualified Data.Text as T import qualified Data.Text.IO as T -import System.FilePath ((), takeBaseName) +import System.FilePath ((), takeBaseName, splitDirectories) import System.Directory (doesDirectoryExist, pathIsSymbolicLink) import System.Directory.PathWalk import Validation (Validation (..)) -import Security.Advisories (Advisory, AttributeOverridePolicy (NoOverrides), OutOfBandAttributes (..), ParseAdvisoryError, parseAdvisory) +import Security.Advisories (Advisory, AttributeOverridePolicy (NoOverrides), OutOfBandAttributes (..), ParseAdvisoryError, parseAdvisory, Ecosystem(..)) import Security.Advisories.Core.HsecId (HsecId, parseHsecId, placeholder) import Security.Advisories.Git(firstAppearanceCommitDate, getAdvisoryGitInfo, lastModificationCommitDate) import Control.Monad.Except (runExceptT, ExceptT (ExceptT), withExceptT) -import Security.Advisories.Parse (OOBError(GitHasNoOOB)) +import Security.Advisories.Parse (OOBError(GitHasNoOOB, PathHasNoEcosystem)) +import Security.Advisories.Core.Advisory (ghcComponentFromText) dirNameAdvisories :: FilePath @@ -136,14 +139,17 @@ advisoryFromFile :: (MonadIO m) => FilePath -> m (Validation ParseAdvisoryError Advisory) advisoryFromFile advisoryPath = do - oob <- runExceptT $ withExceptT GitHasNoOOB $ do + oob <- runExceptT $ do + ecosystem <- parseEcosystem advisoryPath + withExceptT GitHasNoOOB $ do gitInfo <- ExceptT $ liftIO $ getAdvisoryGitInfo advisoryPath pure OutOfBandAttributes { oobPublished = firstAppearanceCommitDate gitInfo , oobModified = lastModificationCommitDate gitInfo + , oobEcosystem = ecosystem } fileContent <- liftIO $ T.readFile advisoryPath - pure + pure $ either Failure Success $ parseAdvisory NoOverrides oob fileContent @@ -169,3 +175,10 @@ _forFiles root go = case parseHsecId (takeBaseName file) of Nothing -> pure mempty Just hsid -> go (dir file) hsid + +parseEcosystem :: Monad m => FilePath -> ExceptT OOBError m (Maybe Ecosystem) +parseEcosystem fp = ExceptT . pure $ case drop 1 $ reverse $ splitDirectories fp of + package : "hackage" : _ -> pure (Just $ Hackage $ T.pack package) + component : "ghc" : _ | Just ghc <- ghcComponentFromText (T.pack component) -> pure (Just $ GHC ghc) + _ : _ : "advisories" : _ -> Left PathHasNoEcosystem + _ -> pure Nothing diff --git a/code/hsec-tools/src/Security/Advisories/Generate/HTML.hs b/code/hsec-tools/src/Security/Advisories/Generate/HTML.hs index 2ef4dc33..e3e16ec7 100644 --- a/code/hsec-tools/src/Security/Advisories/Generate/HTML.hs +++ b/code/hsec-tools/src/Security/Advisories/Generate/HTML.hs @@ -35,6 +35,7 @@ import Validation (Validation (..)) import qualified Security.Advisories as Advisories import Security.Advisories.Filesystem (listAdvisories) import Security.Advisories.Generate.TH (readDirFilesTH) +import Security.Advisories.Core.Advisory (Ecosystem (..), ghcComponentToText) -- * Actions @@ -87,7 +88,7 @@ data AdvisoryR = AdvisoryR deriving stock (Show) data AffectedPackageR = AffectedPackageR - { packageName :: Text, + { ecosystem :: Ecosystem, introduced :: Text, fixed :: Maybe Text } @@ -118,6 +119,11 @@ listByDates advisories = td_ [class_ "advisory-packages"] $ toHtml $ T.intercalate "," $ packageName <$> advisoryAffected advisory td_ [class_ "advisory-summary"] $ toHtml $ advisorySummary advisory +packageName :: AffectedPackageR -> Text +packageName af = case ecosystem af of + Hackage n -> n + GHC c -> "ghc:" <> ghcComponentToText c + listByPackages :: [AdvisoryR] -> Html () listByPackages advisories = inPage PageListByPackages $ do @@ -231,7 +237,7 @@ toAdvisoryR x = toAffectedPackageR p = flip map (Advisories.affectedVersions p) $ \versionRange -> AffectedPackageR - { packageName = Advisories.affectedPackage p, + { ecosystem = Advisories.affectedEcosystem p, introduced = T.pack $ prettyShow $ Advisories.affectedVersionRangeIntroduced versionRange, fixed = T.pack . prettyShow <$> Advisories.affectedVersionRangeFixed versionRange } diff --git a/code/hsec-tools/src/Security/Advisories/Parse.hs b/code/hsec-tools/src/Security/Advisories/Parse.hs index e5389506..ecdf942a 100644 --- a/code/hsec-tools/src/Security/Advisories/Parse.hs +++ b/code/hsec-tools/src/Security/Advisories/Parse.hs @@ -24,6 +24,10 @@ import Data.Bifunctor (first) import Data.Foldable (toList) import Data.Maybe (fromMaybe) import Data.Monoid (First(..)) + +import Data.Tuple (swap) +import Control.Applicative ((<|>)) + import GHC.Generics (Generic) import Data.Sequence (Seq((:<|))) @@ -56,14 +60,10 @@ type OOB = Either OOBError OutOfBandAttributes -- | A source of attributes supplied out of band from the advisory -- content. Values provided out of band are treated according to -- the 'AttributeOverridePolicy'. --- --- The convenient way to construct a value of this type is to start --- with 'emptyOutOfBandAttributes', then use the record accessors to --- set particular fields. --- data OutOfBandAttributes = OutOfBandAttributes { oobModified :: UTCTime , oobPublished :: UTCTime + , oobEcosystem :: Maybe Ecosystem } deriving (Show) @@ -81,8 +81,8 @@ data ParseAdvisoryError deriving stock (Eq, Show, Generic) -- | @since 0.2.0.0 -instance Exception ParseAdvisoryError where - displayException = T.unpack . \case +instance Exception ParseAdvisoryError where + displayException = T.unpack . \case MarkdownError _ explanation -> "Markdown parsing error:\n" <> explanation MarkdownFormatError explanation -> "Markdown structure error:\n" <> explanation TomlError _ explanation -> "Couldn't parse front matter as TOML:\n" <> explanation @@ -91,14 +91,16 @@ instance Exception ParseAdvisoryError where -- | errors that may occur while ingesting oob data -- -- @since 0.2.0.0 -data OOBError +data OOBError = StdInHasNoOOB -- ^ we obtain the advisory via stdin and can hence not parse git history + | PathHasNoEcosystem -- ^ the path is missing 'hackage' or 'ghc' directory | GitHasNoOOB GitError -- ^ processing oob info via git failed deriving stock (Eq, Show, Generic) -displayOOBError :: OOBError -> String -displayOOBError = \case +displayOOBError :: OOBError -> String +displayOOBError = \case StdInHasNoOOB -> "stdin doesn't provide out of band information" + PathHasNoEcosystem -> "the path is missing 'hackage' or 'ghc' directory" GitHasNoOOB gitErr -> "no out of band information obtained with git error:\n" <> explainGitError gitErr @@ -186,6 +188,10 @@ parseAdvisoryTable oob policy doc summary details html tab = (oobPublished <$> oob) "advisory.modified" (amdModified (frontMatterAdvisory fm)) + let affected = frontMatterAffected fm + case oob of + Right (OutOfBandAttributes _ _ (Just ecosystem)) -> validateEcosystem ecosystem affected + _ -> pure () pure Advisory { advisoryId = amdId (frontMatterAdvisory fm) , advisoryPublished = published @@ -195,7 +201,7 @@ parseAdvisoryTable oob policy doc summary details html tab = , advisoryKeywords = amdKeywords (frontMatterAdvisory fm) , advisoryAliases = amdAliases (frontMatterAdvisory fm) , advisoryRelated = amdRelated (frontMatterAdvisory fm) - , advisoryAffected = frontMatterAffected fm + , advisoryAffected = affected , advisoryReferences = frontMatterReferences fm , advisoryPandoc = doc , advisoryHtml = html @@ -274,10 +280,10 @@ mergeOobMandatory -> m a mergeOobMandatory policy eoob doob k ib = mergeOob policy eoob k ib everythingFailed pure - where - everythingFailed e = fail $ unlines - [ "while trying to lookup mandatory key " <> show k <> ":" - , doob e + where + everythingFailed e = fail $ unlines + [ "while trying to lookup mandatory key " <> show k <> ":" + , doob e ] {- | A solution to an awkward problem: how to delete the TOML diff --git a/code/hsec-tools/src/Security/Advisories/Queries.hs b/code/hsec-tools/src/Security/Advisories/Queries.hs index cae48c7b..22323e44 100644 --- a/code/hsec-tools/src/Security/Advisories/Queries.hs +++ b/code/hsec-tools/src/Security/Advisories/Queries.hs @@ -38,9 +38,10 @@ isAffectedByHelper checkWithRange queryPackageName queryVersionish = any checkAffected . advisoryAffected where checkAffected :: Affected -> Bool - checkAffected affected = - queryPackageName == affectedPackage affected - && checkWithRange queryVersionish (fromAffected affected) + checkAffected affected = case affectedEcosystem affected of + Hackage pkg -> queryPackageName == pkg && checkWithRange queryVersionish (fromAffected affected) + -- TODO: support GHC ecosystem query, e.g. by adding a cli flag + _ -> False fromAffected :: Affected -> VersionRange fromAffected = foldr (unionVersionRanges . fromAffectedVersionRange) noVersion . affectedVersions diff --git a/code/hsec-tools/test/Spec.hs b/code/hsec-tools/test/Spec.hs index 9c157b2e..01d01f52 100644 --- a/code/hsec-tools/test/Spec.hs +++ b/code/hsec-tools/test/Spec.hs @@ -44,9 +44,10 @@ doGoldenTest fp = goldenVsString fp (fp <> ".golden") (LText.encodeUtf8 <$> doCh doCheck = do input <- T.readFile fp let fakeDate = UTCTime (fromOrdinalDate 1970 0) 0 - attr = OutOfBandAttributes + attr = OutOfBandAttributes { oobPublished = fakeDate , oobModified = fakeDate + , oobEcosystem = Nothing } res = parseAdvisory NoOverrides (Right attr) input osvExport = case res of diff --git a/code/hsec-tools/test/Spec/QueriesSpec.hs b/code/hsec-tools/test/Spec/QueriesSpec.hs index 6c8137f9..80b105f5 100644 --- a/code/hsec-tools/test/Spec/QueriesSpec.hs +++ b/code/hsec-tools/test/Spec/QueriesSpec.hs @@ -115,7 +115,7 @@ mkAdvisory versionRange = , advisoryRelated = [ "CVE-2022-YYYY" , "CVE-2022-ZZZZ" ] , advisoryAffected = [ Affected - { affectedPackage = packageName + { affectedEcosystem = Hackage packageName , affectedCVSS = cvss , affectedVersions = mkAffectedVersions versionRange , affectedArchitectures = Nothing diff --git a/code/hsec-tools/test/golden/EXAMPLE_ADVISORY.md.golden b/code/hsec-tools/test/golden/EXAMPLE_ADVISORY.md.golden index 14695047..681af2e0 100644 --- a/code/hsec-tools/test/golden/EXAMPLE_ADVISORY.md.golden +++ b/code/hsec-tools/test/golden/EXAMPLE_ADVISORY.md.golden @@ -17,7 +17,7 @@ Right ] , advisoryAffected = [ Affected - { affectedPackage = "package-name" + { affectedEcosystem = Hackage "package-name" , affectedCVSS = CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H , affectedVersions = [ AffectedVersionRange From 88b026b3b2751221dbe9f00521404a98144ad782 Mon Sep 17 00:00:00 2001 From: Tristan Cacqueray Date: Fri, 19 Jul 2024 23:40:54 -0400 Subject: [PATCH 2/7] Rebase Advisory.Ecosystem change --- .../src/Security/Advisories/Core/Advisory.hs | 2 +- code/hsec-tools/app/Main.hs | 8 +------ .../src/Security/Advisories/Format.hs | 21 +++++++++++++++---- .../src/Security/Advisories/Parse.hs | 7 +++++++ code/hsec-tools/test/Spec/FormatSpec.hs | 11 +++++++++- 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs index cf9d6aa6..ca3f3175 100644 --- a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs +++ b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs @@ -53,7 +53,7 @@ data Ecosystem = Hackage Text | GHC GHCComponent -- Keep this list in sync with the 'ghcComponentFromText' below data GHCComponent = GHCCompiler | GHCi | GHCRTS - deriving stock (Show, Eq) + deriving stock (Show, Eq, Enum, Bounded) ghcComponentToText :: GHCComponent -> Text ghcComponentToText c = case c of diff --git a/code/hsec-tools/app/Main.hs b/code/hsec-tools/app/Main.hs index ac80133c..95ec61a4 100644 --- a/code/hsec-tools/app/Main.hs +++ b/code/hsec-tools/app/Main.hs @@ -23,18 +23,12 @@ import Security.Advisories.Generate.HTML import Security.Advisories.Generate.Snapshot import Security.Advisories.Git import Security.Advisories.Queries (listVersionRangeAffectedBy) - +import Security.Advisories.Filesystem (parseEcosystem) import System.Exit (die, exitFailure, exitSuccess) import System.FilePath (takeBaseName) import System.IO (hPrint, hPutStrLn, stderr) import Validation (Validation (..)) -import Security.Advisories.Generate.HTML -import Security.Advisories.Filesystem (parseEcosystem) - -import qualified Command.Reserve - - main :: IO () main = join $ diff --git a/code/hsec-tools/src/Security/Advisories/Format.hs b/code/hsec-tools/src/Security/Advisories/Format.hs index 7107522d..b7c58f07 100644 --- a/code/hsec-tools/src/Security/Advisories/Format.hs +++ b/code/hsec-tools/src/Security/Advisories/Format.hs @@ -15,6 +15,7 @@ module Security.Advisories.Format ) where +import Control.Applicative ((<|>)) import Commonmark.Types (HasAttributes (..), IsBlock (..), IsInline (..), Rangeable (..), SourceRange (..)) import qualified Data.Map as Map import Data.Maybe (fromMaybe) @@ -135,16 +136,25 @@ instance Toml.ToTable AdvisoryMetadata where ["aliases" Toml..= amdAliases x | not (null (amdAliases x))] ++ ["related" Toml..= amdRelated x | not (null (amdRelated x))] +instance Toml.FromValue GHCComponent where + fromValue v = case v of + Toml.Text' _ n + | Just c <- ghcComponentFromText n -> pure c + _ -> Toml.failAt (Toml.valueAnn v) "Invalid component, expected compiler|ghci|rts" + +instance Toml.ToValue GHCComponent where + toValue = Toml.Text' () . ghcComponentToText + instance Toml.FromValue Affected where fromValue = Toml.parseTableFromValue $ - do package <- Toml.reqKey "package" + do ecosystem <- (Hackage <$> Toml.reqKey "package") <|> (GHC <$> Toml.reqKey "ghc-component") cvss <- Toml.reqKey "cvss" -- TODO validate CVSS format os <- Toml.optKey "os" arch <- Toml.optKey "arch" decls <- maybe [] Map.toList <$> Toml.optKey "declarations" versions <- Toml.reqKey "versions" pure $ Affected - { affectedPackage = package + { affectedEcosystem = ecosystem , affectedCVSS = cvss , affectedVersions = versions , affectedArchitectures = arch @@ -157,14 +167,17 @@ instance Toml.ToValue Affected where instance Toml.ToTable Affected where toTable x = Toml.table $ - [ "package" Toml..= affectedPackage x - , "cvss" Toml..= affectedCVSS x + ecosystem ++ + [ "cvss" Toml..= affectedCVSS x , "versions" Toml..= affectedVersions x ] ++ [ "os" Toml..= y | Just y <- [affectedOS x]] ++ [ "arch" Toml..= y | Just y <- [affectedArchitectures x]] ++ [ "declarations" Toml..= asTable (affectedDeclarations x) | not (null (affectedDeclarations x))] where + ecosystem = case affectedEcosystem x of + Hackage pkg -> ["package" Toml..= pkg] + GHC c -> ["ghc-component" Toml..= c] asTable kvs = Map.fromList [(T.unpack k, v) | (k,v) <- kvs] instance Toml.FromValue AffectedVersionRange where diff --git a/code/hsec-tools/src/Security/Advisories/Parse.hs b/code/hsec-tools/src/Security/Advisories/Parse.hs index ecdf942a..ff4500e3 100644 --- a/code/hsec-tools/src/Security/Advisories/Parse.hs +++ b/code/hsec-tools/src/Security/Advisories/Parse.hs @@ -16,6 +16,7 @@ , displayOOBError , AttributeOverridePolicy(..) , ParseAdvisoryError(..) + , validateEcosystem ) where @@ -209,6 +210,12 @@ parseAdvisoryTable oob policy doc summary details html tab = , advisoryDetails = details } +-- | Make sure one of the affected match the ecosystem +validateEcosystem :: MonadFail m => Ecosystem -> [Affected] -> m () +validateEcosystem ecosystem xs + | any (\affected -> affectedEcosystem affected == ecosystem) xs = pure () + | otherwise = fail $ "Expected an affected to match the ecosystem: " <> show ecosystem + advisoryDoc :: Blocks -> Either Text (Text, [Block]) advisoryDoc (Many blocks) = case blocks of CodeBlock (_, classes, _) frontMatter :<| t diff --git a/code/hsec-tools/test/Spec/FormatSpec.hs b/code/hsec-tools/test/Spec/FormatSpec.hs index 5aac95e6..b40f05c5 100644 --- a/code/hsec-tools/test/Spec/FormatSpec.hs +++ b/code/hsec-tools/test/Spec/FormatSpec.hs @@ -70,13 +70,22 @@ genAdvisoryMetadata = genAffected :: Gen.Gen Affected genAffected = Affected - <$> genText + <$> genEcosystem <*> genCVSS <*> Gen.list (Range.linear 0 5) genAffectedVersionRange <*> Gen.maybe (Gen.list (Range.linear 0 5) genArchitecture) <*> Gen.maybe (Gen.list (Range.linear 0 5) genOS) <*> (Map.toList . Map.fromList <$> Gen.list (Range.linear 0 5) ((,) <$> genText <*> genVersionRange)) +genEcosystem :: Gen.Gen Ecosystem +genEcosystem = Gen.choice $ + [ Hackage <$> genText + , GHC <$> genGHCComponent + ] + +genGHCComponent :: Gen.Gen GHCComponent +genGHCComponent = Gen.choice $ map pure [minBound..maxBound] + genCVSS :: Gen.Gen CVSS genCVSS = Gen.choice $ From 8ee041edde6b0c049c43b352a6e9b1b6a58a8c7e Mon Sep 17 00:00:00 2001 From: Tristan Cacqueray Date: Fri, 19 Jul 2024 23:49:37 -0400 Subject: [PATCH 3/7] Rename Ecosystem into ComponentIdentifier --- .../src/Security/Advisories/Core/Advisory.hs | 6 +++--- code/hsec-tools/app/Main.hs | 6 +++--- .../src/Security/Advisories/Convert/OSV.hs | 4 ++-- .../src/Security/Advisories/Filesystem.hs | 16 ++++++++-------- .../hsec-tools/src/Security/Advisories/Format.hs | 4 ++-- .../src/Security/Advisories/Generate/HTML.hs | 6 +++--- code/hsec-tools/src/Security/Advisories/Parse.hs | 16 ++++++++-------- .../src/Security/Advisories/Queries.hs | 2 +- code/hsec-tools/test/Spec.hs | 2 +- code/hsec-tools/test/Spec/FormatSpec.hs | 6 +++--- code/hsec-tools/test/Spec/QueriesSpec.hs | 2 +- .../test/golden/EXAMPLE_ADVISORY.md.golden | 2 +- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs index ca3f3175..546f913e 100644 --- a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs +++ b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs @@ -10,7 +10,7 @@ module Security.Advisories.Core.Advisory , AffectedVersionRange(..) , OS(..) , Keyword(..) - , Ecosystem(..) + , ComponentIdentifier(..) , GHCComponent(..) , ghcComponentToText , ghcComponentFromText @@ -48,7 +48,7 @@ data Advisory = Advisory } deriving stock (Show) -data Ecosystem = Hackage Text | GHC GHCComponent +data ComponentIdentifier = Hackage Text | GHC GHCComponent deriving stock (Show, Eq) -- Keep this list in sync with the 'ghcComponentFromText' below @@ -71,7 +71,7 @@ ghcComponentFromText c = case c of -- | An affected package (or package component). An 'Advisory' must -- mention one or more packages. data Affected = Affected - { affectedEcosystem :: Ecosystem + { affectedComponentIdentifier :: ComponentIdentifier , affectedCVSS :: CVSS.CVSS , affectedVersions :: [AffectedVersionRange] , affectedArchitectures :: Maybe [Architecture] diff --git a/code/hsec-tools/app/Main.hs b/code/hsec-tools/app/Main.hs index 95ec61a4..2163aead 100644 --- a/code/hsec-tools/app/Main.hs +++ b/code/hsec-tools/app/Main.hs @@ -23,7 +23,7 @@ import Security.Advisories.Generate.HTML import Security.Advisories.Generate.Snapshot import Security.Advisories.Git import Security.Advisories.Queries (listVersionRangeAffectedBy) -import Security.Advisories.Filesystem (parseEcosystem) +import Security.Advisories.Filesystem (parseComponentIdentifier) import System.Exit (die, exitFailure, exitSuccess) import System.FilePath (takeBaseName) import System.IO (hPrint, hPutStrLn, stderr) @@ -169,13 +169,13 @@ withAdvisory go file = do oob <- runExceptT $ case file of Nothing -> throwE StdInHasNoOOB Just path -> do - ecosystem <- parseEcosystem path + ecosystem <- parseComponentIdentifier path withExceptT GitHasNoOOB $ do gitInfo <- ExceptT $ liftIO $ getAdvisoryGitInfo path pure OutOfBandAttributes { oobPublished = firstAppearanceCommitDate gitInfo , oobModified = lastModificationCommitDate gitInfo - , oobEcosystem = ecosystem + , oobComponentIdentifier = ecosystem } case parseAdvisory NoOverrides oob input of diff --git a/code/hsec-tools/src/Security/Advisories/Convert/OSV.hs b/code/hsec-tools/src/Security/Advisories/Convert/OSV.hs index 10be9484..c63e0e72 100644 --- a/code/hsec-tools/src/Security/Advisories/Convert/OSV.hs +++ b/code/hsec-tools/src/Security/Advisories/Convert/OSV.hs @@ -30,14 +30,14 @@ convert adv = mkAffected :: Affected -> OSV.Affected Void Void Void mkAffected aff = OSV.Affected - { OSV.affectedPackage = mkPackage (affectedEcosystem aff) + { OSV.affectedPackage = mkPackage (affectedComponentIdentifier aff) , OSV.affectedRanges = pure $ mkRange (affectedVersions aff) , OSV.affectedSeverity = [OSV.Severity (affectedCVSS aff)] , OSV.affectedEcosystemSpecific = Nothing , OSV.affectedDatabaseSpecific = Nothing } -mkPackage :: Ecosystem -> OSV.Package +mkPackage :: ComponentIdentifier -> OSV.Package mkPackage ecosystem = OSV.Package { OSV.packageName = packageName , OSV.packageEcosystem = ecosystemName diff --git a/code/hsec-tools/src/Security/Advisories/Filesystem.hs b/code/hsec-tools/src/Security/Advisories/Filesystem.hs index 3f7a28e5..897a7d2d 100644 --- a/code/hsec-tools/src/Security/Advisories/Filesystem.hs +++ b/code/hsec-tools/src/Security/Advisories/Filesystem.hs @@ -21,7 +21,7 @@ module Security.Advisories.Filesystem , forAdvisory , listAdvisories , advisoryFromFile - , parseEcosystem + , parseComponentIdentifier ) where import Control.Applicative (liftA2) @@ -39,11 +39,11 @@ import System.Directory (doesDirectoryExist, pathIsSymbolicLink) import System.Directory.PathWalk import Validation (Validation (..)) -import Security.Advisories (Advisory, AttributeOverridePolicy (NoOverrides), OutOfBandAttributes (..), ParseAdvisoryError, parseAdvisory, Ecosystem(..)) +import Security.Advisories (Advisory, AttributeOverridePolicy (NoOverrides), OutOfBandAttributes (..), ParseAdvisoryError, parseAdvisory, ComponentIdentifier(..)) import Security.Advisories.Core.HsecId (HsecId, parseHsecId, placeholder) import Security.Advisories.Git(firstAppearanceCommitDate, getAdvisoryGitInfo, lastModificationCommitDate) import Control.Monad.Except (runExceptT, ExceptT (ExceptT), withExceptT) -import Security.Advisories.Parse (OOBError(GitHasNoOOB, PathHasNoEcosystem)) +import Security.Advisories.Parse (OOBError(GitHasNoOOB, PathHasNoComponentIdentifier)) import Security.Advisories.Core.Advisory (ghcComponentFromText) @@ -140,13 +140,13 @@ advisoryFromFile => FilePath -> m (Validation ParseAdvisoryError Advisory) advisoryFromFile advisoryPath = do oob <- runExceptT $ do - ecosystem <- parseEcosystem advisoryPath + ecosystem <- parseComponentIdentifier advisoryPath withExceptT GitHasNoOOB $ do gitInfo <- ExceptT $ liftIO $ getAdvisoryGitInfo advisoryPath pure OutOfBandAttributes { oobPublished = firstAppearanceCommitDate gitInfo , oobModified = lastModificationCommitDate gitInfo - , oobEcosystem = ecosystem + , oobComponentIdentifier = ecosystem } fileContent <- liftIO $ T.readFile advisoryPath pure @@ -176,9 +176,9 @@ _forFiles root go = Nothing -> pure mempty Just hsid -> go (dir file) hsid -parseEcosystem :: Monad m => FilePath -> ExceptT OOBError m (Maybe Ecosystem) -parseEcosystem fp = ExceptT . pure $ case drop 1 $ reverse $ splitDirectories fp of +parseComponentIdentifier :: Monad m => FilePath -> ExceptT OOBError m (Maybe ComponentIdentifier) +parseComponentIdentifier fp = ExceptT . pure $ case drop 1 $ reverse $ splitDirectories fp of package : "hackage" : _ -> pure (Just $ Hackage $ T.pack package) component : "ghc" : _ | Just ghc <- ghcComponentFromText (T.pack component) -> pure (Just $ GHC ghc) - _ : _ : "advisories" : _ -> Left PathHasNoEcosystem + _ : _ : "advisories" : _ -> Left PathHasNoComponentIdentifier _ -> pure Nothing diff --git a/code/hsec-tools/src/Security/Advisories/Format.hs b/code/hsec-tools/src/Security/Advisories/Format.hs index b7c58f07..a2fff806 100644 --- a/code/hsec-tools/src/Security/Advisories/Format.hs +++ b/code/hsec-tools/src/Security/Advisories/Format.hs @@ -154,7 +154,7 @@ instance Toml.FromValue Affected where decls <- maybe [] Map.toList <$> Toml.optKey "declarations" versions <- Toml.reqKey "versions" pure $ Affected - { affectedEcosystem = ecosystem + { affectedComponentIdentifier = ecosystem , affectedCVSS = cvss , affectedVersions = versions , affectedArchitectures = arch @@ -175,7 +175,7 @@ instance Toml.ToTable Affected where [ "arch" Toml..= y | Just y <- [affectedArchitectures x]] ++ [ "declarations" Toml..= asTable (affectedDeclarations x) | not (null (affectedDeclarations x))] where - ecosystem = case affectedEcosystem x of + ecosystem = case affectedComponentIdentifier x of Hackage pkg -> ["package" Toml..= pkg] GHC c -> ["ghc-component" Toml..= c] asTable kvs = Map.fromList [(T.unpack k, v) | (k,v) <- kvs] diff --git a/code/hsec-tools/src/Security/Advisories/Generate/HTML.hs b/code/hsec-tools/src/Security/Advisories/Generate/HTML.hs index e3e16ec7..b28cfc6b 100644 --- a/code/hsec-tools/src/Security/Advisories/Generate/HTML.hs +++ b/code/hsec-tools/src/Security/Advisories/Generate/HTML.hs @@ -35,7 +35,7 @@ import Validation (Validation (..)) import qualified Security.Advisories as Advisories import Security.Advisories.Filesystem (listAdvisories) import Security.Advisories.Generate.TH (readDirFilesTH) -import Security.Advisories.Core.Advisory (Ecosystem (..), ghcComponentToText) +import Security.Advisories.Core.Advisory (ComponentIdentifier (..), ghcComponentToText) -- * Actions @@ -88,7 +88,7 @@ data AdvisoryR = AdvisoryR deriving stock (Show) data AffectedPackageR = AffectedPackageR - { ecosystem :: Ecosystem, + { ecosystem :: ComponentIdentifier, introduced :: Text, fixed :: Maybe Text } @@ -237,7 +237,7 @@ toAdvisoryR x = toAffectedPackageR p = flip map (Advisories.affectedVersions p) $ \versionRange -> AffectedPackageR - { ecosystem = Advisories.affectedEcosystem p, + { ecosystem = Advisories.affectedComponentIdentifier p, introduced = T.pack $ prettyShow $ Advisories.affectedVersionRangeIntroduced versionRange, fixed = T.pack . prettyShow <$> Advisories.affectedVersionRangeFixed versionRange } diff --git a/code/hsec-tools/src/Security/Advisories/Parse.hs b/code/hsec-tools/src/Security/Advisories/Parse.hs index ff4500e3..b16f7f78 100644 --- a/code/hsec-tools/src/Security/Advisories/Parse.hs +++ b/code/hsec-tools/src/Security/Advisories/Parse.hs @@ -16,7 +16,7 @@ , displayOOBError , AttributeOverridePolicy(..) , ParseAdvisoryError(..) - , validateEcosystem + , validateComponentIdentifier ) where @@ -64,7 +64,7 @@ type OOB = Either OOBError OutOfBandAttributes data OutOfBandAttributes = OutOfBandAttributes { oobModified :: UTCTime , oobPublished :: UTCTime - , oobEcosystem :: Maybe Ecosystem + , oobComponentIdentifier :: Maybe ComponentIdentifier } deriving (Show) @@ -94,14 +94,14 @@ instance Exception ParseAdvisoryError where -- @since 0.2.0.0 data OOBError = StdInHasNoOOB -- ^ we obtain the advisory via stdin and can hence not parse git history - | PathHasNoEcosystem -- ^ the path is missing 'hackage' or 'ghc' directory + | PathHasNoComponentIdentifier -- ^ the path is missing 'hackage' or 'ghc' directory | GitHasNoOOB GitError -- ^ processing oob info via git failed deriving stock (Eq, Show, Generic) displayOOBError :: OOBError -> String displayOOBError = \case StdInHasNoOOB -> "stdin doesn't provide out of band information" - PathHasNoEcosystem -> "the path is missing 'hackage' or 'ghc' directory" + PathHasNoComponentIdentifier -> "the path is missing 'hackage' or 'ghc' directory" GitHasNoOOB gitErr -> "no out of band information obtained with git error:\n" <> explainGitError gitErr @@ -191,7 +191,7 @@ parseAdvisoryTable oob policy doc summary details html tab = (amdModified (frontMatterAdvisory fm)) let affected = frontMatterAffected fm case oob of - Right (OutOfBandAttributes _ _ (Just ecosystem)) -> validateEcosystem ecosystem affected + Right (OutOfBandAttributes _ _ (Just ecosystem)) -> validateComponentIdentifier ecosystem affected _ -> pure () pure Advisory { advisoryId = amdId (frontMatterAdvisory fm) @@ -211,9 +211,9 @@ parseAdvisoryTable oob policy doc summary details html tab = } -- | Make sure one of the affected match the ecosystem -validateEcosystem :: MonadFail m => Ecosystem -> [Affected] -> m () -validateEcosystem ecosystem xs - | any (\affected -> affectedEcosystem affected == ecosystem) xs = pure () +validateComponentIdentifier :: MonadFail m => ComponentIdentifier -> [Affected] -> m () +validateComponentIdentifier ecosystem xs + | any (\affected -> affectedComponentIdentifier affected == ecosystem) xs = pure () | otherwise = fail $ "Expected an affected to match the ecosystem: " <> show ecosystem advisoryDoc :: Blocks -> Either Text (Text, [Block]) diff --git a/code/hsec-tools/src/Security/Advisories/Queries.hs b/code/hsec-tools/src/Security/Advisories/Queries.hs index 22323e44..06ed2b25 100644 --- a/code/hsec-tools/src/Security/Advisories/Queries.hs +++ b/code/hsec-tools/src/Security/Advisories/Queries.hs @@ -38,7 +38,7 @@ isAffectedByHelper checkWithRange queryPackageName queryVersionish = any checkAffected . advisoryAffected where checkAffected :: Affected -> Bool - checkAffected affected = case affectedEcosystem affected of + checkAffected affected = case affectedComponentIdentifier affected of Hackage pkg -> queryPackageName == pkg && checkWithRange queryVersionish (fromAffected affected) -- TODO: support GHC ecosystem query, e.g. by adding a cli flag _ -> False diff --git a/code/hsec-tools/test/Spec.hs b/code/hsec-tools/test/Spec.hs index 01d01f52..836e19ec 100644 --- a/code/hsec-tools/test/Spec.hs +++ b/code/hsec-tools/test/Spec.hs @@ -47,7 +47,7 @@ doGoldenTest fp = goldenVsString fp (fp <> ".golden") (LText.encodeUtf8 <$> doCh attr = OutOfBandAttributes { oobPublished = fakeDate , oobModified = fakeDate - , oobEcosystem = Nothing + , oobComponentIdentifier = Nothing } res = parseAdvisory NoOverrides (Right attr) input osvExport = case res of diff --git a/code/hsec-tools/test/Spec/FormatSpec.hs b/code/hsec-tools/test/Spec/FormatSpec.hs index b40f05c5..efd9345e 100644 --- a/code/hsec-tools/test/Spec/FormatSpec.hs +++ b/code/hsec-tools/test/Spec/FormatSpec.hs @@ -70,15 +70,15 @@ genAdvisoryMetadata = genAffected :: Gen.Gen Affected genAffected = Affected - <$> genEcosystem + <$> genComponentIdentifier <*> genCVSS <*> Gen.list (Range.linear 0 5) genAffectedVersionRange <*> Gen.maybe (Gen.list (Range.linear 0 5) genArchitecture) <*> Gen.maybe (Gen.list (Range.linear 0 5) genOS) <*> (Map.toList . Map.fromList <$> Gen.list (Range.linear 0 5) ((,) <$> genText <*> genVersionRange)) -genEcosystem :: Gen.Gen Ecosystem -genEcosystem = Gen.choice $ +genComponentIdentifier :: Gen.Gen ComponentIdentifier +genComponentIdentifier = Gen.choice $ [ Hackage <$> genText , GHC <$> genGHCComponent ] diff --git a/code/hsec-tools/test/Spec/QueriesSpec.hs b/code/hsec-tools/test/Spec/QueriesSpec.hs index 80b105f5..54be3d90 100644 --- a/code/hsec-tools/test/Spec/QueriesSpec.hs +++ b/code/hsec-tools/test/Spec/QueriesSpec.hs @@ -115,7 +115,7 @@ mkAdvisory versionRange = , advisoryRelated = [ "CVE-2022-YYYY" , "CVE-2022-ZZZZ" ] , advisoryAffected = [ Affected - { affectedEcosystem = Hackage packageName + { affectedComponentIdentifier = Hackage packageName , affectedCVSS = cvss , affectedVersions = mkAffectedVersions versionRange , affectedArchitectures = Nothing diff --git a/code/hsec-tools/test/golden/EXAMPLE_ADVISORY.md.golden b/code/hsec-tools/test/golden/EXAMPLE_ADVISORY.md.golden index 681af2e0..62dda0c3 100644 --- a/code/hsec-tools/test/golden/EXAMPLE_ADVISORY.md.golden +++ b/code/hsec-tools/test/golden/EXAMPLE_ADVISORY.md.golden @@ -17,7 +17,7 @@ Right ] , advisoryAffected = [ Affected - { affectedEcosystem = Hackage "package-name" + { affectedComponentIdentifier = Hackage "package-name" , affectedCVSS = CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H , affectedVersions = [ AffectedVersionRange From 0840d717267e7bedf0c385d65752ce9512320ffd Mon Sep 17 00:00:00 2001 From: Tristan Cacqueray Date: Fri, 19 Jul 2024 23:53:51 -0400 Subject: [PATCH 4/7] Add extra GHCComponents --- .../src/Security/Advisories/Core/Advisory.hs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs index 546f913e..a49f0c79 100644 --- a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs +++ b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs @@ -52,7 +52,7 @@ data ComponentIdentifier = Hackage Text | GHC GHCComponent deriving stock (Show, Eq) -- Keep this list in sync with the 'ghcComponentFromText' below -data GHCComponent = GHCCompiler | GHCi | GHCRTS +data GHCComponent = GHCCompiler | GHCi | GHCRTS | GHCPkg | RunGHC | IServ | HP2PS | HPC | HSC2HS | Haddock deriving stock (Show, Eq, Enum, Bounded) ghcComponentToText :: GHCComponent -> Text @@ -60,12 +60,26 @@ ghcComponentToText c = case c of GHCCompiler -> "compiler" GHCi -> "ghci" GHCRTS -> "rts" + GHCPkg -> "ghc-pkg" + RunGHC -> "runghc" + IServ -> "iserv" + HP2PS -> "hp2ps" + HPC -> "hpc" + HSC2HS -> "hsc2hs" + Haddock -> "haddock" ghcComponentFromText :: Text -> Maybe GHCComponent ghcComponentFromText c = case c of "compiler" -> Just GHCCompiler "ghci" -> Just GHCi "rts" -> Just GHCRTS + "ghc-pkg" -> Just GHCPkg + "runghc" -> Just RunGHC + "iserv" -> Just IServ + "hp2ps" -> Just HP2PS + "hpc" -> Just HPC + "hsc2hs" -> Just HSC2HS + "haddock" -> Just Haddock _ -> Nothing -- | An affected package (or package component). An 'Advisory' must From 0292b2a609bb49dcc802b8340c2ce326689fb75c Mon Sep 17 00:00:00 2001 From: Tristan Cacqueray Date: Sat, 20 Jul 2024 09:06:05 -0400 Subject: [PATCH 5/7] Make GHCComponent match the command name and improve the error message --- code/hsec-core/src/Security/Advisories/Core/Advisory.hs | 8 ++++---- code/hsec-tools/src/Security/Advisories/Format.hs | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs index a49f0c79..92ef84d7 100644 --- a/code/hsec-core/src/Security/Advisories/Core/Advisory.hs +++ b/code/hsec-core/src/Security/Advisories/Core/Advisory.hs @@ -57,12 +57,12 @@ data GHCComponent = GHCCompiler | GHCi | GHCRTS | GHCPkg | RunGHC | IServ | HP2P ghcComponentToText :: GHCComponent -> Text ghcComponentToText c = case c of - GHCCompiler -> "compiler" + GHCCompiler -> "ghc" GHCi -> "ghci" GHCRTS -> "rts" GHCPkg -> "ghc-pkg" RunGHC -> "runghc" - IServ -> "iserv" + IServ -> "ghc-iserv" HP2PS -> "hp2ps" HPC -> "hpc" HSC2HS -> "hsc2hs" @@ -70,12 +70,12 @@ ghcComponentToText c = case c of ghcComponentFromText :: Text -> Maybe GHCComponent ghcComponentFromText c = case c of - "compiler" -> Just GHCCompiler + "ghc" -> Just GHCCompiler "ghci" -> Just GHCi "rts" -> Just GHCRTS "ghc-pkg" -> Just GHCPkg "runghc" -> Just RunGHC - "iserv" -> Just IServ + "ghc-iserv" -> Just IServ "hp2ps" -> Just HP2PS "hpc" -> Just HPC "hsc2hs" -> Just HSC2HS diff --git a/code/hsec-tools/src/Security/Advisories/Format.hs b/code/hsec-tools/src/Security/Advisories/Format.hs index a2fff806..b5f80078 100644 --- a/code/hsec-tools/src/Security/Advisories/Format.hs +++ b/code/hsec-tools/src/Security/Advisories/Format.hs @@ -140,7 +140,9 @@ instance Toml.FromValue GHCComponent where fromValue v = case v of Toml.Text' _ n | Just c <- ghcComponentFromText n -> pure c - _ -> Toml.failAt (Toml.valueAnn v) "Invalid component, expected compiler|ghci|rts" + _ -> Toml.failAt (Toml.valueAnn v) $ T.unpack $ "Invalid component, expected " <> T.intercalate "|" componentNames + where + componentNames = map ghcComponentToText [minBound..maxBound] instance Toml.ToValue GHCComponent where toValue = Toml.Text' () . ghcComponentToText From 2a09d024c72a35ce5139df9b0cd1c87ca151537c Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Sat, 27 Jul 2024 11:12:16 +1000 Subject: [PATCH 6/7] improve GHCComponent parse errors --- code/hsec-tools/src/Security/Advisories/Format.hs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/code/hsec-tools/src/Security/Advisories/Format.hs b/code/hsec-tools/src/Security/Advisories/Format.hs index b5f80078..19037c53 100644 --- a/code/hsec-tools/src/Security/Advisories/Format.hs +++ b/code/hsec-tools/src/Security/Advisories/Format.hs @@ -139,8 +139,17 @@ instance Toml.ToTable AdvisoryMetadata where instance Toml.FromValue GHCComponent where fromValue v = case v of Toml.Text' _ n - | Just c <- ghcComponentFromText n -> pure c - _ -> Toml.failAt (Toml.valueAnn v) $ T.unpack $ "Invalid component, expected " <> T.intercalate "|" componentNames + | Just c <- ghcComponentFromText n + -> pure c + | otherwise + -> Toml.failAt (Toml.valueAnn v) $ + "Invalid ghc-component '" + <> T.unpack n + <> "', expected " + <> T.unpack (T.intercalate "|" componentNames) + _ -> Toml.failAt (Toml.valueAnn v) $ + "Non-text ghc-component, expected" + <> T.unpack (T.intercalate "|" componentNames) where componentNames = map ghcComponentToText [minBound..maxBound] From 1c9dcc2af56cabe57529ce8301fee510973caa3e Mon Sep 17 00:00:00 2001 From: Tristan Cacqueray Date: Tue, 30 Jul 2024 06:43:21 -0400 Subject: [PATCH 7/7] Use Hedgehog.Gen.enumBounded --- code/hsec-tools/test/Spec/FormatSpec.hs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/code/hsec-tools/test/Spec/FormatSpec.hs b/code/hsec-tools/test/Spec/FormatSpec.hs index efd9345e..8a4e1d7a 100644 --- a/code/hsec-tools/test/Spec/FormatSpec.hs +++ b/code/hsec-tools/test/Spec/FormatSpec.hs @@ -80,12 +80,9 @@ genAffected = genComponentIdentifier :: Gen.Gen ComponentIdentifier genComponentIdentifier = Gen.choice $ [ Hackage <$> genText - , GHC <$> genGHCComponent + , GHC <$> Gen.enumBounded ] -genGHCComponent :: Gen.Gen GHCComponent -genGHCComponent = Gen.choice $ map pure [minBound..maxBound] - genCVSS :: Gen.Gen CVSS genCVSS = Gen.choice $