hsec-core (empty) → 0.1.0.0
raw patch · 5 files changed
+281/−0 lines, 5 filesdep +Cabal-syntaxdep +basedep +cvss
Dependencies added: Cabal-syntax, base, cvss, hsec-core, osv, pandoc-types, safe, tasty, tasty-hunit, text, time
Files
- CHANGELOG.md +0/−0
- hsec-core.cabal +67/−0
- src/Security/Advisories/Core/Advisory.hs +111/−0
- src/Security/Advisories/Core/HsecId.hs +93/−0
- test/Spec.hs +10/−0
+ CHANGELOG.md view
+ hsec-core.cabal view
@@ -0,0 +1,67 @@+cabal-version: 2.4+name: hsec-core+version: 0.1.0.0++-- A short (one-line) description of the package.+synopsis: Core package representing Haskell advisories++-- A longer description of the package.+description: Core package representing Haskell advisories.++-- A URL where users can report bugs.+-- bug-reports:++-- The license under which the package is released.+license: BSD-3-Clause+author: Haskell Security Response Team+maintainer: security-advisories@haskell.org++-- A copyright notice.+-- copyright:+category: Data+extra-doc-files: CHANGELOG.md++tested-with:+ GHC ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.8 || ==9.6.3 || ==9.8.1++library+ exposed-modules:+ Security.Advisories.Core.Advisory+ Security.Advisories.Core.HsecId++ build-depends:+ , base >=4.14 && <4.20+ , Cabal-syntax >=3.8.1.0 && <3.11+ , cvss >= 0.1 && < 0.2+ , osv >= 0.1 && < 0.2+ , pandoc-types >=1.22 && <2+ , safe >=0.3+ , text >=1.2 && <3+ , time >=1.9 && <1.14++ -- , commonmark ^>=0.2.2+ -- , commonmark-pandoc >=0.2 && <0.3+ -- , containers >=0.6 && <0.7+ -- , mtl >=2.2 && <2.4+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options:+ -Wall -Wcompat -Widentities -Wincomplete-record-updates+ -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints++test-suite spec+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends:+ , base <5+ , cvss+ , hsec-core+ , tasty <1.5+ , tasty-hunit <0.11+ , text++ default-language: Haskell2010+ ghc-options:+ -Wall -Wcompat -Widentities -Wincomplete-record-updates+ -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints
+ src/Security/Advisories/Core/Advisory.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE DerivingVia #-}++module Security.Advisories.Core.Advisory+ ( Advisory(..)+ -- * Supporting types+ , Affected(..)+ , CAPEC(..)+ , CWE(..)+ , Architecture(..)+ , AffectedVersionRange(..)+ , OS(..)+ , Keyword(..)+ )+ where++import Data.Text (Text)+import Data.Time (ZonedTime)+import Distribution.Types.Version (Version)+import Distribution.Types.VersionRange (VersionRange)++import Text.Pandoc.Definition (Pandoc)++import Security.Advisories.Core.HsecId+import qualified Security.CVSS as CVSS+import Security.OSV (Reference)++data Advisory = Advisory+ { advisoryId :: HsecId+ , advisoryModified :: ZonedTime+ , advisoryPublished :: ZonedTime+ , advisoryCAPECs :: [CAPEC]+ , advisoryCWEs :: [CWE]+ , advisoryKeywords :: [Keyword]+ , advisoryAliases :: [Text]+ , advisoryRelated :: [Text]+ , advisoryAffected :: [Affected]+ , advisoryReferences :: [Reference]+ , advisoryPandoc :: Pandoc -- ^ Parsed document, without TOML front matter+ , advisoryHtml :: Text+ , advisorySummary :: Text+ -- ^ A one-line, English textual summary of the vulnerability+ , advisoryDetails :: Text+ -- ^ Details of the vulnerability (CommonMark), without TOML front matter+ }+ deriving stock (Show)++-- | An affected package (or package component). An 'Advisory' must+-- mention one or more packages.+data Affected = Affected+ { affectedPackage :: Text+ , affectedCVSS :: CVSS.CVSS+ , affectedVersions :: [AffectedVersionRange]+ , affectedArchitectures :: Maybe [Architecture]+ , affectedOS :: Maybe [OS]+ , affectedDeclarations :: [(Text, VersionRange)]+ }+ deriving stock (Show)++newtype CAPEC = CAPEC {unCAPEC :: Integer}+ deriving stock (Show)++newtype CWE = CWE {unCWE :: Integer}+ deriving stock (Show)++data Architecture+ = AArch64+ | Alpha+ | Arm+ | HPPA+ | HPPA1_1+ | I386+ | IA64+ | M68K+ | MIPS+ | MIPSEB+ | MIPSEL+ | NIOS2+ | PowerPC+ | PowerPC64+ | PowerPC64LE+ | RISCV32+ | RISCV64+ | RS6000+ | S390+ | S390X+ | SH4+ | SPARC+ | SPARC64+ | VAX+ | X86_64+ deriving stock (Show)++data OS+ = Windows+ | MacOS+ | Linux+ | FreeBSD+ | Android+ | NetBSD+ | OpenBSD+ deriving stock (Show)++newtype Keyword = Keyword Text+ deriving stock (Eq, Ord)+ deriving (Show) via Text++data AffectedVersionRange = AffectedVersionRange+ { affectedVersionRangeIntroduced :: Version,+ affectedVersionRangeFixed :: Maybe Version+ }+ deriving stock (Show)
+ src/Security/Advisories/Core/HsecId.hs view
@@ -0,0 +1,93 @@+module Security.Advisories.Core.HsecId+ (+ HsecId+ , hsecIdYear+ , hsecIdSerial+ , mkHsecId+ , placeholder+ , isPlaceholder+ , parseHsecId+ , printHsecId+ , nextHsecId+ , getNextHsecId+ ) where++import Control.Monad (guard, join)++import Data.Time (getCurrentTime, utctDay)+import Data.Time.Calendar.OrdinalDate (toOrdinalDate)++import Safe (readMay)++data HsecId = HsecId Integer Integer+ deriving (Eq, Ord)++instance Show HsecId where+ show = printHsecId++-- | Make an 'HsecId'. Year and serial must both be positive, or+-- else both must be zero (the 'placeholder').+mkHsecId+ :: Integer -- ^ Year+ -> Integer -- ^ Serial number within year+ -> Maybe HsecId+mkHsecId y n+ | y > 0 && n > 0 || y == 0 && n == 0 = Just $ HsecId y n+ | otherwise = Nothing++hsecIdYear :: HsecId -> Integer+hsecIdYear (HsecId y _) = y++hsecIdSerial :: HsecId -> Integer+hsecIdSerial (HsecId _ n) = n++-- | The placeholder ID: __HSEC-0000-0000__.+-- See also 'isPlaceholder'.+placeholder :: HsecId+placeholder = HsecId 0 0++-- | Test whether an ID is the 'placeholder'+isPlaceholder :: HsecId -> Bool+isPlaceholder = (==) placeholder++-- | Parse an 'HsecId'. The 'placeholder' is accepted.+parseHsecId :: String -> Maybe HsecId+parseHsecId s = case s of+ 'H':'S':'E':'C':'-':t ->+ let+ (y, t') = break (== '-') t+ n = drop 1 t'+ in do+ guard $ length y >= 4 -- year must have at least 4 digits+ guard $ length n >= 4 -- serial must have at least 4 digits+ join $ mkHsecId <$> readMay y <*> readMay n+ _ -> Nothing++printHsecId :: HsecId -> String+printHsecId (HsecId y n) = "HSEC-" <> pad (show y) <> "-" <> pad (show n)+ where+ pad s = replicate (4 - length s) '0' <> s++-- | Given a year and an HSEC ID, return a larger HSEC ID. This+-- function, when given the current year and the greatest allocated+-- HSEC ID, returns the next HSEC ID to allocate.+--+nextHsecId+ :: Integer -- ^ Current year+ -> HsecId+ -> HsecId+nextHsecId curYear (HsecId idYear n)+ | curYear > idYear = HsecId curYear 1+ | otherwise = HsecId idYear (n + 1)++-- | Get the current time, and return an HSEC ID greater than the+-- given HSEC ID. The year of the returned HSEC ID is the current+-- year.+--+getNextHsecId+ :: HsecId+ -> IO HsecId+getNextHsecId oldId = do+ t <- getCurrentTime+ let (year, _dayOfYear) = toOrdinalDate (utctDay t)+ pure $ nextHsecId year oldId
+ test/Spec.hs view
@@ -0,0 +1,10 @@+module Main where++import Test.Tasty++main :: IO ()+main =+ defaultMain $+ testGroup+ "Tests"+ []