packages feed

hsec-tools 0.4.0.0 → 0.5.0.0

raw patch · 11 files changed

+167/−10 lines, 11 filesdep ~bytestringdep ~hsec-corePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: bytestring, hsec-core

API changes (from Hackage documentation)

+ Security.Advisories.Convert.OSV: HsecEcosystemSpecific :: [AffectedApi] -> HsecEcosystemSpecific
+ Security.Advisories.Convert.OSV: [hsecEcosystemAffectedApi] :: HsecEcosystemSpecific -> [AffectedApi]
+ Security.Advisories.Convert.OSV: instance Data.Aeson.Types.FromJSON.FromJSON Security.Advisories.Convert.OSV.AffectedLinks
+ Security.Advisories.Convert.OSV: instance Data.Aeson.Types.FromJSON.FromJSON Security.Advisories.Convert.OSV.DbLinks
+ Security.Advisories.Convert.OSV: instance Data.Aeson.Types.FromJSON.FromJSON Security.Advisories.Convert.OSV.HsecEcosystemSpecific
+ Security.Advisories.Convert.OSV: instance Data.Aeson.Types.FromJSON.FromJSON Security.Advisories.Core.Advisory.AffectedApi
+ Security.Advisories.Convert.OSV: instance Data.Aeson.Types.ToJSON.ToJSON Security.Advisories.Convert.OSV.HsecEcosystemSpecific
+ Security.Advisories.Convert.OSV: instance Data.Aeson.Types.ToJSON.ToJSON Security.Advisories.Core.Advisory.AffectedApi
+ Security.Advisories.Convert.OSV: instance GHC.Classes.Eq Security.Advisories.Convert.OSV.HsecEcosystemSpecific
+ Security.Advisories.Convert.OSV: instance GHC.Show.Show Security.Advisories.Convert.OSV.HsecEcosystemSpecific
+ Security.Advisories.Convert.OSV: newtype HsecEcosystemSpecific
+ Security.Advisories.Format: instance Toml.Schema.FromValue.FromValue Security.Advisories.Core.Advisory.AffectedApi
+ Security.Advisories.Format: instance Toml.Schema.ToValue.ToTable Security.Advisories.Core.Advisory.AffectedApi
+ Security.Advisories.Format: instance Toml.Schema.ToValue.ToValue Security.Advisories.Core.Advisory.AffectedApi
- Security.Advisories.Convert.OSV: convert :: Advisory -> Model Void Void Void Void
+ Security.Advisories.Convert.OSV: convert :: Advisory -> Model Void Void HsecEcosystemSpecific Void
- Security.Advisories.Convert.OSV: convertWithLinks :: DbLinks -> Advisory -> Model DbLinks AffectedLinks Void Void
+ Security.Advisories.Convert.OSV: convertWithLinks :: DbLinks -> Advisory -> Model DbLinks AffectedLinks HsecEcosystemSpecific Void

Files

CHANGELOG.md view
@@ -1,3 +1,10 @@+## 0.5.0.0++* Add `HsecEcosystemSpecific` with `affected_api` in OSV exports+* Add TOML parsing/rendering for `api` key in affected sections+* Add HTML rendering for affected APIs+* Update `hsec-core` to `0.5.0.0`+ ## 0.4.0.0  * Update `hsec-core` to `0.4.0.0`
hsec-tools.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               hsec-tools-version:            0.4.0.0+version:            0.5.0.0  -- A short (one-line) description of the package. synopsis:@@ -73,7 +73,7 @@     , extra                 >=1.7      && <1.9     , file-embed            >=0.0.13.0 && <0.0.17     , filepath              >=1.4      && <1.6-    , hsec-core             >=0.4.0.0  && <0.5+    , hsec-core             >=0.5.0.0  && <0.6     , lens                  >=5.1.0    && <5.4     , lucid                 >=2.9.0    && <3     , mtl                   >=2.2      && <2.4@@ -120,7 +120,7 @@     , Cabal-syntax          >=3.8.1.0 && <3.15     , directory             <2     , filepath              >=1.4     && <1.6-    , hsec-core             >=0.4.0.0 && <0.5+    , hsec-core             >=0.5.0.0 && <0.6     , hsec-tools     , network-uri           >=2.6.3.0 && <2.8     , optparse-applicative  >=0.17    && <0.19@@ -142,10 +142,13 @@   other-modules:     Paths_hsec_tools     Spec.FormatSpec+    Spec.OsvSpec    build-depends:+    , aeson           >=2.0.1.0 && <3     , aeson-pretty    <2     , base+    , bytestring      >=0.10     && <0.14     , Cabal-syntax     , containers     , cvss
src/Security/Advisories/Convert/OSV.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} @@ -6,6 +8,7 @@     convertWithLinks,     DbLinks (..),     AffectedLinks (..),+    HsecEcosystemSpecific (..),     haskellLinks,   ) where@@ -18,7 +21,7 @@ import Security.Advisories.Core.OsvId (printOsvId) import qualified Security.OSV as OSV -convert :: Advisory -> OSV.Model Void Void Void Void+convert :: Advisory -> OSV.Model Void Void HsecEcosystemSpecific Void convert adv =   ( OSV.newModel'       (T.pack . printHsecId $ advisoryId adv)@@ -33,13 +36,16 @@       OSV.modelAffected = fmap mkAffected (advisoryAffected adv)     } -mkAffected :: Affected -> OSV.Affected Void Void Void+mkAffected :: Affected -> OSV.Affected Void HsecEcosystemSpecific Void mkAffected aff =   OSV.Affected     { OSV.affectedPackage = mkPackage (affectedComponentIdentifier aff),       OSV.affectedRanges = pure $ mkRange (affectedVersions aff),       OSV.affectedSeverity = [OSV.Severity (affectedCVSS aff)],-      OSV.affectedEcosystemSpecific = Nothing,+      OSV.affectedEcosystemSpecific =+        if null (affectedApi aff)+          then Nothing+          else Just (HsecEcosystemSpecific (affectedApi aff)),       OSV.affectedDatabaseSpecific = Nothing     } @@ -66,7 +72,7 @@       OSV.EventIntroduced (T.pack $ prettyShow $ affectedVersionRangeIntroduced range)         : maybe [] (pure . OSV.EventFixed . T.pack . prettyShow) (affectedVersionRangeFixed range) -convertWithLinks :: DbLinks -> Advisory -> OSV.Model DbLinks AffectedLinks Void Void+convertWithLinks :: DbLinks -> Advisory -> OSV.Model DbLinks AffectedLinks HsecEcosystemSpecific Void convertWithLinks links adv =   OSV.Model     { OSV.modelDatabaseSpecific = Just links,@@ -90,6 +96,13 @@         "home" .= dbLinksHome       ] +instance FromJSON DbLinks where+  parseJSON = withObject "DbLinks" $ \o -> do+    dbLinksRepository <- o .: "repository"+    dbLinksOSVs <- o .: "osvs"+    dbLinksHome <- o .: "home"+    pure DbLinks {..}+ haskellLinks :: DbLinks haskellLinks =   DbLinks@@ -110,7 +123,42 @@         "human_link" .= affectedLinksHumanLink       ] -mkAffectedWithLinks :: DbLinks -> HsecId -> Affected -> OSV.Affected AffectedLinks Void Void+instance FromJSON AffectedLinks where+  parseJSON = withObject "AffectedLinks" $ \o -> do+    affectedLinksOSV <- o .: "osv"+    affectedLinksHumanLink <- o .: "human_link"+    pure AffectedLinks {..}++newtype HsecEcosystemSpecific = HsecEcosystemSpecific+  { hsecEcosystemAffectedApi :: [AffectedApi]+  }+  deriving stock (Eq, Show)++instance ToJSON HsecEcosystemSpecific where+  toJSON (HsecEcosystemSpecific apis) =+    object+      [ "affected_api" .= apis+      ]++instance FromJSON HsecEcosystemSpecific where+  parseJSON = withObject "HsecEcosystemSpecific" $ \o -> do+    hsecEcosystemAffectedApi <- o .: "affected_api"+    pure HsecEcosystemSpecific {..}++instance ToJSON AffectedApi where+  toJSON AffectedApi {..} =+    object+      [ "module" .= affectedApiModule,+        "name" .= affectedApiName+      ]++instance FromJSON AffectedApi where+  parseJSON = withObject "AffectedApi" $ \o -> do+    affectedApiModule <- o .: "module"+    affectedApiName <- o .: "name"+    pure AffectedApi {..}++mkAffectedWithLinks :: DbLinks -> HsecId -> Affected -> OSV.Affected AffectedLinks HsecEcosystemSpecific Void mkAffectedWithLinks links hsecId aff =   OSV.Affected     { OSV.affectedDatabaseSpecific =
src/Security/Advisories/Format.hs view
@@ -176,6 +176,7 @@       os <- Toml.optKey "os"       arch <- Toml.optKey "arch"       decls <- maybe [] Map.toList <$> Toml.optKey "declarations"+      api <- fromMaybe [] <$> Toml.optKey "api"       versions <- Toml.reqKey "versions"       pure $         Affected@@ -184,7 +185,8 @@             affectedVersions = versions,             affectedArchitectures = arch,             affectedOS = os,-            affectedDeclarations = decls+            affectedDeclarations = decls,+            affectedApi = api           }  instance Toml.ToValue Affected where@@ -200,6 +202,7 @@         ++ ["os" Toml..= y | Just y <- [affectedOS x]]         ++ ["arch" Toml..= y | Just y <- [affectedArchitectures x]]         ++ ["declarations" Toml..= asTable (affectedDeclarations x) | not (null (affectedDeclarations x))]+        ++ ["api" Toml..= affectedApi x | not (null (affectedApi x))]     where       ecosystem = case affectedComponentIdentifier x of         Repository repoUrl repoName pkg@@ -207,6 +210,25 @@           | otherwise -> ["repository-url" Toml..= repoUrl, "repository-name" Toml..= repoName, "package" Toml..= pkg]         GHC c -> ["ghc-component" Toml..= c]       asTable kvs = Map.fromList [(T.unpack k, v) | (k, v) <- kvs]++instance Toml.FromValue AffectedApi where+  fromValue = Toml.parseTableFromValue $ do+    mod <- Toml.reqKey "module"+    name <- Toml.reqKey "name"+    pure AffectedApi+      { affectedApiModule = mod,+        affectedApiName = name+      }++instance Toml.ToValue AffectedApi where+  toValue = Toml.defaultTableToValue++instance Toml.ToTable AffectedApi where+  toTable x =+    Toml.table+      [ "module" Toml..= affectedApiModule x,+        "name" Toml..= affectedApiName x+      ]  instance Toml.FromValue AffectedVersionRange where   fromValue = Toml.parseTableFromValue $
src/Security/Advisories/Generate/HTML.hs view
@@ -265,6 +265,13 @@               code_ [] $ toHtml declaration               ": "               code_ [] $ toHtml $ T.pack $ prettyShow versionRange+        dt_ "Affected APIs"+        placeholderWhenEmptyOr (Advisories.affectedApi affected) $ \apis ->+          forM_ apis $ \api ->+            dd_ [] $ do+              code_ [] $ toHtml $ Advisories.affectedApiModule api+              "."+              code_ [] $ toHtml $ Advisories.affectedApiName api  -- * Utils 
test/Spec.hs view
@@ -13,6 +13,7 @@ import qualified Security.Advisories.Convert.OSV as OSV import Security.Advisories.Parse import qualified Spec.FormatSpec as FormatSpec+import qualified Spec.OsvSpec as OsvSpec import System.Directory (listDirectory) import Test.Tasty (defaultMain, testGroup, TestTree) import Test.Tasty.Golden (goldenVsString)@@ -26,6 +27,7 @@             "Tests"             [ goldenTestsSpec goldenFiles             , FormatSpec.spec+            , OsvSpec.spec             ]  listGoldenFiles :: IO [FilePath]
test/Spec/FormatSpec.hs view
@@ -79,6 +79,7 @@     <*> 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))+    <*> Gen.list (Range.linear 0 5) genAffectedApi  genComponentIdentifier :: Gen.Gen ComponentIdentifier genComponentIdentifier =@@ -139,7 +140,7 @@ genUTCTime =   UTCTime     <$> genDay-    <*> fmap secondsToDiffTime (Gen.integral $ Range.constant 0 86401)+    <*> fmap secondsToDiffTime (Gen.integral $ Range.constant 0 86399)  genDay :: Gen.Gen Day genDay = do@@ -205,3 +206,6 @@       ]   entry <- Gen.text (Range.linear 4 10) Gen.alphaNum   pure . Maybe.fromJust . parseOsvId $ prefix <> "-" <> entry++genAffectedApi :: Gen.Gen AffectedApi+genAffectedApi = AffectedApi <$> genText <*> genText
+ test/Spec/OsvSpec.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE OverloadedStrings #-}++module Spec.OsvSpec (spec) where++import Data.Aeson (decode, encode)+import Data.ByteString.Lazy (toStrict)+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import Security.Advisories.Convert.OSV+import Security.Advisories.Core.Advisory+import Test.Tasty+import Test.Tasty.Hedgehog+import qualified Hedgehog as Gen+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range++spec :: TestTree+spec =+  testGroup+    "OSV"+    [ testGroup+        "AffectedApi"+        [ testProperty "JSON roundtrip" $+            Gen.property $ do+              api <- Gen.forAll genAffectedApi+              let encoded = encode api+                  decoded = decode encoded+              Just api Gen.=== decoded+        ]+    , testGroup+        "HsecEcosystemSpecific"+        [ testProperty "JSON roundtrip" $+            Gen.property $ do+              ecs <- Gen.forAll genHsecEcosystemSpecific+              let encoded = encode ecs+                  decoded = decode encoded+              Just ecs Gen.=== decoded+        , testProperty "encodes affected_api key" $+            Gen.property $ do+              apis <- Gen.forAll (Gen.list (Range.linear 1 5) genAffectedApi)+              let ecs = HsecEcosystemSpecific apis+                  jsonBytes = encode ecs+                  jsonText = TE.decodeUtf8 (toStrict jsonBytes)+              Gen.assert $ "affected_api" `T.isInfixOf` jsonText+        , testProperty "Nothing when empty" $+            Gen.property $ do+              let ecs = HsecEcosystemSpecific []+                  jsonBytes = encode ecs+                  jsonText = TE.decodeUtf8 (toStrict jsonBytes)+              Gen.assert $ "affected_api" `T.isInfixOf` jsonText+        ]+    ]++genAffectedApi :: Gen.Gen AffectedApi+genAffectedApi = AffectedApi <$> genText <*> genText++genHsecEcosystemSpecific :: Gen.Gen HsecEcosystemSpecific+genHsecEcosystemSpecific = HsecEcosystemSpecific <$> Gen.list (Range.linear 0 5) genAffectedApi++genText :: Gen.Gen T.Text+genText = Gen.text (Range.linear 1 20) Gen.alphaNum
test/golden/EXAMPLE_ADVISORY_GHC.md.golden view
@@ -46,6 +46,7 @@                 , affectedArchitectures = Nothing                 , affectedOS = Nothing                 , affectedDeclarations = []+                , affectedApi = []                 }             ]         , advisoryReferences =
test/golden/EXAMPLE_ADVISORY_HACKAGE.md.golden view
@@ -51,6 +51,7 @@                 , affectedArchitectures = Nothing                 , affectedOS = Nothing                 , affectedDeclarations = []+                , affectedApi = []                 }             ]         , advisoryReferences =
test/golden/EXAMPLE_ADVISORY_REPO.md.golden view
@@ -51,6 +51,7 @@                 , affectedArchitectures = Nothing                 , affectedOS = Nothing                 , affectedDeclarations = []+                , affectedApi = []                 }             ]         , advisoryReferences =