{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module CVE
  ( parseFeed,
    CPE (..),
    CPEMatch (..),
    CPEMatchRow (..),
    cpeMatches,
    CVE (..),
    CVEID,
    cveLI,
  )
where

import Data.Aeson
  ( FromJSON,
    Object,
    eitherDecode,
    parseJSON,
    withObject,
    (.!=),
    (.:),
    (.:!),
  )
import Data.Aeson.Types (Parser, prependFailure)
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.List (intercalate)
import qualified Data.Text as T
import Data.Time.Clock (UTCTime)
import Database.SQLite.Simple (FromRow, ToRow, field, fromRow, toRow)
import Database.SQLite.Simple.ToField (toField)
import OurPrelude
import Utils (Boundary (..), VersionMatcher (..))

type CVEID = Text

data CVE = CVE
  { CVE -> CVEID
cveID :: CVEID,
    CVE -> [CPEMatch]
cveCPEMatches :: [CPEMatch],
    CVE -> CVEID
cveDescription :: Text,
    CVE -> UTCTime
cvePublished :: UTCTime,
    CVE -> UTCTime
cveLastModified :: UTCTime
  }
  deriving (Int -> CVE -> ShowS
[CVE] -> ShowS
CVE -> String
(Int -> CVE -> ShowS)
-> (CVE -> String) -> ([CVE] -> ShowS) -> Show CVE
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CVE] -> ShowS
$cshowList :: [CVE] -> ShowS
show :: CVE -> String
$cshow :: CVE -> String
showsPrec :: Int -> CVE -> ShowS
$cshowsPrec :: Int -> CVE -> ShowS
Show, CVE -> CVE -> Bool
(CVE -> CVE -> Bool) -> (CVE -> CVE -> Bool) -> Eq CVE
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CVE -> CVE -> Bool
$c/= :: CVE -> CVE -> Bool
== :: CVE -> CVE -> Bool
$c== :: CVE -> CVE -> Bool
Eq, Eq CVE
Eq CVE
-> (CVE -> CVE -> Ordering)
-> (CVE -> CVE -> Bool)
-> (CVE -> CVE -> Bool)
-> (CVE -> CVE -> Bool)
-> (CVE -> CVE -> Bool)
-> (CVE -> CVE -> CVE)
-> (CVE -> CVE -> CVE)
-> Ord CVE
CVE -> CVE -> Bool
CVE -> CVE -> Ordering
CVE -> CVE -> CVE
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: CVE -> CVE -> CVE
$cmin :: CVE -> CVE -> CVE
max :: CVE -> CVE -> CVE
$cmax :: CVE -> CVE -> CVE
>= :: CVE -> CVE -> Bool
$c>= :: CVE -> CVE -> Bool
> :: CVE -> CVE -> Bool
$c> :: CVE -> CVE -> Bool
<= :: CVE -> CVE -> Bool
$c<= :: CVE -> CVE -> Bool
< :: CVE -> CVE -> Bool
$c< :: CVE -> CVE -> Bool
compare :: CVE -> CVE -> Ordering
$ccompare :: CVE -> CVE -> Ordering
$cp1Ord :: Eq CVE
Ord)

-- | cve list item
cveLI :: CVE -> Bool -> Text
cveLI :: CVE -> Bool -> CVEID
cveLI CVE
c Bool
patched =
  CVEID
"- ["
    CVEID -> CVEID -> CVEID
forall a. Semigroup a => a -> a -> a
<> CVE -> CVEID
cveID CVE
c
    CVEID -> CVEID -> CVEID
forall a. Semigroup a => a -> a -> a
<> CVEID
"](https://nvd.nist.gov/vuln/detail/"
    CVEID -> CVEID -> CVEID
forall a. Semigroup a => a -> a -> a
<> CVE -> CVEID
cveID CVE
c
    CVEID -> CVEID -> CVEID
forall a. Semigroup a => a -> a -> a
<> CVEID
")"
    CVEID -> CVEID -> CVEID
forall a. Semigroup a => a -> a -> a
<> CVEID
p
  where
    p :: CVEID
p =
      if Bool
patched
        then CVEID
" (patched)"
        else CVEID
""

data CPEMatch = CPEMatch
  { CPEMatch -> CPE
cpeMatchCPE :: CPE,
    CPEMatch -> Bool
cpeMatchVulnerable :: Bool,
    CPEMatch -> VersionMatcher
cpeMatchVersionMatcher :: VersionMatcher
  }
  deriving (Int -> CPEMatch -> ShowS
[CPEMatch] -> ShowS
CPEMatch -> String
(Int -> CPEMatch -> ShowS)
-> (CPEMatch -> String) -> ([CPEMatch] -> ShowS) -> Show CPEMatch
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CPEMatch] -> ShowS
$cshowList :: [CPEMatch] -> ShowS
show :: CPEMatch -> String
$cshow :: CPEMatch -> String
showsPrec :: Int -> CPEMatch -> ShowS
$cshowsPrec :: Int -> CPEMatch -> ShowS
Show, CPEMatch -> CPEMatch -> Bool
(CPEMatch -> CPEMatch -> Bool)
-> (CPEMatch -> CPEMatch -> Bool) -> Eq CPEMatch
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CPEMatch -> CPEMatch -> Bool
$c/= :: CPEMatch -> CPEMatch -> Bool
== :: CPEMatch -> CPEMatch -> Bool
$c== :: CPEMatch -> CPEMatch -> Bool
Eq, Eq CPEMatch
Eq CPEMatch
-> (CPEMatch -> CPEMatch -> Ordering)
-> (CPEMatch -> CPEMatch -> Bool)
-> (CPEMatch -> CPEMatch -> Bool)
-> (CPEMatch -> CPEMatch -> Bool)
-> (CPEMatch -> CPEMatch -> Bool)
-> (CPEMatch -> CPEMatch -> CPEMatch)
-> (CPEMatch -> CPEMatch -> CPEMatch)
-> Ord CPEMatch
CPEMatch -> CPEMatch -> Bool
CPEMatch -> CPEMatch -> Ordering
CPEMatch -> CPEMatch -> CPEMatch
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: CPEMatch -> CPEMatch -> CPEMatch
$cmin :: CPEMatch -> CPEMatch -> CPEMatch
max :: CPEMatch -> CPEMatch -> CPEMatch
$cmax :: CPEMatch -> CPEMatch -> CPEMatch
>= :: CPEMatch -> CPEMatch -> Bool
$c>= :: CPEMatch -> CPEMatch -> Bool
> :: CPEMatch -> CPEMatch -> Bool
$c> :: CPEMatch -> CPEMatch -> Bool
<= :: CPEMatch -> CPEMatch -> Bool
$c<= :: CPEMatch -> CPEMatch -> Bool
< :: CPEMatch -> CPEMatch -> Bool
$c< :: CPEMatch -> CPEMatch -> Bool
compare :: CPEMatch -> CPEMatch -> Ordering
$ccompare :: CPEMatch -> CPEMatch -> Ordering
$cp1Ord :: Eq CPEMatch
Ord)

instance FromRow CPEMatch where
  fromRow :: RowParser CPEMatch
fromRow = do
    CPE
cpeMatchCPE <- RowParser CPE
forall a. FromRow a => RowParser a
fromRow
    let cpeMatchVulnerable :: Bool
cpeMatchVulnerable = Bool
True
    VersionMatcher
cpeMatchVersionMatcher <- RowParser VersionMatcher
forall a. FromField a => RowParser a
field
    CPEMatch -> RowParser CPEMatch
forall (f :: * -> *) a. Applicative f => a -> f a
pure CPEMatch :: CPE -> Bool -> VersionMatcher -> CPEMatch
CPEMatch {Bool
VersionMatcher
CPE
cpeMatchVersionMatcher :: VersionMatcher
cpeMatchVulnerable :: Bool
cpeMatchCPE :: CPE
cpeMatchVersionMatcher :: VersionMatcher
cpeMatchVulnerable :: Bool
cpeMatchCPE :: CPE
..}

-- This decodes an entire CPE string and related attributes, but we only use
-- cpeVulnerable, cpeProduct, cpeVersion and cpeMatcher.
data CPE = CPE
  { CPE -> Maybe CVEID
cpePart :: (Maybe Text),
    CPE -> Maybe CVEID
cpeVendor :: (Maybe Text),
    CPE -> Maybe CVEID
cpeProduct :: (Maybe Text),
    CPE -> Maybe CVEID
cpeVersion :: (Maybe Text),
    CPE -> Maybe CVEID
cpeUpdate :: (Maybe Text),
    CPE -> Maybe CVEID
cpeEdition :: (Maybe Text),
    CPE -> Maybe CVEID
cpeLanguage :: (Maybe Text),
    CPE -> Maybe CVEID
cpeSoftwareEdition :: (Maybe Text),
    CPE -> Maybe CVEID
cpeTargetSoftware :: (Maybe Text),
    CPE -> Maybe CVEID
cpeTargetHardware :: (Maybe Text),
    CPE -> Maybe CVEID
cpeOther :: (Maybe Text)
  }
  deriving (CPE -> CPE -> Bool
(CPE -> CPE -> Bool) -> (CPE -> CPE -> Bool) -> Eq CPE
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CPE -> CPE -> Bool
$c/= :: CPE -> CPE -> Bool
== :: CPE -> CPE -> Bool
$c== :: CPE -> CPE -> Bool
Eq, Eq CPE
Eq CPE
-> (CPE -> CPE -> Ordering)
-> (CPE -> CPE -> Bool)
-> (CPE -> CPE -> Bool)
-> (CPE -> CPE -> Bool)
-> (CPE -> CPE -> Bool)
-> (CPE -> CPE -> CPE)
-> (CPE -> CPE -> CPE)
-> Ord CPE
CPE -> CPE -> Bool
CPE -> CPE -> Ordering
CPE -> CPE -> CPE
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: CPE -> CPE -> CPE
$cmin :: CPE -> CPE -> CPE
max :: CPE -> CPE -> CPE
$cmax :: CPE -> CPE -> CPE
>= :: CPE -> CPE -> Bool
$c>= :: CPE -> CPE -> Bool
> :: CPE -> CPE -> Bool
$c> :: CPE -> CPE -> Bool
<= :: CPE -> CPE -> Bool
$c<= :: CPE -> CPE -> Bool
< :: CPE -> CPE -> Bool
$c< :: CPE -> CPE -> Bool
compare :: CPE -> CPE -> Ordering
$ccompare :: CPE -> CPE -> Ordering
$cp1Ord :: Eq CPE
Ord)

instance Show CPE where
  show :: CPE -> String
show
    CPE
      { Maybe CVEID
cpePart :: Maybe CVEID
cpePart :: CPE -> Maybe CVEID
cpePart,
        Maybe CVEID
cpeVendor :: Maybe CVEID
cpeVendor :: CPE -> Maybe CVEID
cpeVendor,
        Maybe CVEID
cpeProduct :: Maybe CVEID
cpeProduct :: CPE -> Maybe CVEID
cpeProduct,
        Maybe CVEID
cpeVersion :: Maybe CVEID
cpeVersion :: CPE -> Maybe CVEID
cpeVersion,
        Maybe CVEID
cpeUpdate :: Maybe CVEID
cpeUpdate :: CPE -> Maybe CVEID
cpeUpdate,
        Maybe CVEID
cpeEdition :: Maybe CVEID
cpeEdition :: CPE -> Maybe CVEID
cpeEdition,
        Maybe CVEID
cpeLanguage :: Maybe CVEID
cpeLanguage :: CPE -> Maybe CVEID
cpeLanguage,
        Maybe CVEID
cpeSoftwareEdition :: Maybe CVEID
cpeSoftwareEdition :: CPE -> Maybe CVEID
cpeSoftwareEdition,
        Maybe CVEID
cpeTargetSoftware :: Maybe CVEID
cpeTargetSoftware :: CPE -> Maybe CVEID
cpeTargetSoftware,
        Maybe CVEID
cpeTargetHardware :: Maybe CVEID
cpeTargetHardware :: CPE -> Maybe CVEID
cpeTargetHardware,
        Maybe CVEID
cpeOther :: Maybe CVEID
cpeOther :: CPE -> Maybe CVEID
cpeOther
      } =
      String
"CPE {"
        String -> ShowS
forall a. Semigroup a => a -> a -> a
<> (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " ([String] -> String)
-> ([[String]] -> [String]) -> [[String]] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat)
          [ String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"part" Maybe CVEID
cpePart,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"vendor" Maybe CVEID
cpeVendor,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"product" Maybe CVEID
cpeProduct,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"version" Maybe CVEID
cpeVersion,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"update" Maybe CVEID
cpeUpdate,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"edition" Maybe CVEID
cpeEdition,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"language" Maybe CVEID
cpeLanguage,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"softwareEdition" Maybe CVEID
cpeSoftwareEdition,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"targetSoftware" Maybe CVEID
cpeTargetSoftware,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"targetHardware" Maybe CVEID
cpeTargetHardware,
            String -> Maybe CVEID -> [String]
forall a. Show a => String -> Maybe a -> [String]
cpeField String
"other" Maybe CVEID
cpeOther
          ]
        String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"}"
      where
        cpeField :: Show a => String -> Maybe a -> [String]
        cpeField :: String -> Maybe a -> [String]
cpeField String
_ Maybe a
Nothing = []
        cpeField String
name (Just a
value) = [String
name String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
" = " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> a -> String
forall a. Show a => a -> String
show a
value]

instance ToRow CPE where
  toRow :: CPE -> [SQLData]
toRow
    CPE
      { Maybe CVEID
cpePart :: Maybe CVEID
cpePart :: CPE -> Maybe CVEID
cpePart,
        Maybe CVEID
cpeVendor :: Maybe CVEID
cpeVendor :: CPE -> Maybe CVEID
cpeVendor,
        Maybe CVEID
cpeProduct :: Maybe CVEID
cpeProduct :: CPE -> Maybe CVEID
cpeProduct,
        Maybe CVEID
cpeVersion :: Maybe CVEID
cpeVersion :: CPE -> Maybe CVEID
cpeVersion,
        Maybe CVEID
cpeUpdate :: Maybe CVEID
cpeUpdate :: CPE -> Maybe CVEID
cpeUpdate,
        Maybe CVEID
cpeEdition :: Maybe CVEID
cpeEdition :: CPE -> Maybe CVEID
cpeEdition,
        Maybe CVEID
cpeLanguage :: Maybe CVEID
cpeLanguage :: CPE -> Maybe CVEID
cpeLanguage,
        Maybe CVEID
cpeSoftwareEdition :: Maybe CVEID
cpeSoftwareEdition :: CPE -> Maybe CVEID
cpeSoftwareEdition,
        Maybe CVEID
cpeTargetSoftware :: Maybe CVEID
cpeTargetSoftware :: CPE -> Maybe CVEID
cpeTargetSoftware,
        Maybe CVEID
cpeTargetHardware :: Maybe CVEID
cpeTargetHardware :: CPE -> Maybe CVEID
cpeTargetHardware,
        Maybe CVEID
cpeOther :: Maybe CVEID
cpeOther :: CPE -> Maybe CVEID
cpeOther
      } =
      (Maybe CVEID -> SQLData) -> [Maybe CVEID] -> [SQLData]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap -- There is no toRow instance for a tuple this large
        Maybe CVEID -> SQLData
forall a. ToField a => a -> SQLData
toField
        [ Maybe CVEID
cpePart,
          Maybe CVEID
cpeVendor,
          Maybe CVEID
cpeProduct,
          Maybe CVEID
cpeVersion,
          Maybe CVEID
cpeUpdate,
          Maybe CVEID
cpeEdition,
          Maybe CVEID
cpeLanguage,
          Maybe CVEID
cpeSoftwareEdition,
          Maybe CVEID
cpeTargetSoftware,
          Maybe CVEID
cpeTargetHardware,
          Maybe CVEID
cpeOther
        ]

instance FromRow CPE where
  fromRow :: RowParser CPE
fromRow = do
    Maybe CVEID
cpePart <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeVendor <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeProduct <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeVersion <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeUpdate <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeEdition <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeLanguage <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeSoftwareEdition <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeTargetSoftware <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeTargetHardware <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    Maybe CVEID
cpeOther <- RowParser (Maybe CVEID)
forall a. FromField a => RowParser a
field
    CPE -> RowParser CPE
forall (f :: * -> *) a. Applicative f => a -> f a
pure CPE :: Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> CPE
CPE {Maybe CVEID
cpeOther :: Maybe CVEID
cpeTargetHardware :: Maybe CVEID
cpeTargetSoftware :: Maybe CVEID
cpeSoftwareEdition :: Maybe CVEID
cpeLanguage :: Maybe CVEID
cpeEdition :: Maybe CVEID
cpeUpdate :: Maybe CVEID
cpeVersion :: Maybe CVEID
cpeProduct :: Maybe CVEID
cpeVendor :: Maybe CVEID
cpePart :: Maybe CVEID
cpeOther :: Maybe CVEID
cpeTargetHardware :: Maybe CVEID
cpeTargetSoftware :: Maybe CVEID
cpeSoftwareEdition :: Maybe CVEID
cpeLanguage :: Maybe CVEID
cpeEdition :: Maybe CVEID
cpeUpdate :: Maybe CVEID
cpeVersion :: Maybe CVEID
cpeProduct :: Maybe CVEID
cpeVendor :: Maybe CVEID
cpePart :: Maybe CVEID
..}

-- | Parse a @description_data@ subtree and return the concatenation of the
-- english descriptions.
parseDescription :: Object -> Parser Text
parseDescription :: Object -> Parser CVEID
parseDescription Object
o = do
  [Object]
dData <- Object
o Object -> CVEID -> Parser [Object]
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"description_data"
  [CVEID]
descriptions <-
    ([[CVEID]] -> [CVEID]) -> Parser [[CVEID]] -> Parser [CVEID]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [[CVEID]] -> [CVEID]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Parser [[CVEID]] -> Parser [CVEID])
-> Parser [[CVEID]] -> Parser [CVEID]
forall a b. (a -> b) -> a -> b
$
      [Parser [CVEID]] -> Parser [[CVEID]]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence ([Parser [CVEID]] -> Parser [[CVEID]])
-> [Parser [CVEID]] -> Parser [[CVEID]]
forall a b. (a -> b) -> a -> b
$
        ((Object -> Parser [CVEID]) -> [Object] -> [Parser [CVEID]])
-> [Object] -> (Object -> Parser [CVEID]) -> [Parser [CVEID]]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Object -> Parser [CVEID]) -> [Object] -> [Parser [CVEID]]
forall a b. (a -> b) -> [a] -> [b]
map [Object]
dData ((Object -> Parser [CVEID]) -> [Parser [CVEID]])
-> (Object -> Parser [CVEID]) -> [Parser [CVEID]]
forall a b. (a -> b) -> a -> b
$
          \Object
dDatum -> do
            CVEID
value <- Object
dDatum Object -> CVEID -> Parser CVEID
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"value"
            CVEID
lang :: Text <- Object
dDatum Object -> CVEID -> Parser CVEID
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"lang"
            [CVEID] -> Parser [CVEID]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([CVEID] -> Parser [CVEID]) -> [CVEID] -> Parser [CVEID]
forall a b. (a -> b) -> a -> b
$
              case CVEID
lang of
                CVEID
"en" -> [CVEID
value]
                CVEID
_ -> []
  CVEID -> Parser CVEID
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CVEID -> Parser CVEID) -> CVEID -> Parser CVEID
forall a b. (a -> b) -> a -> b
$ CVEID -> [CVEID] -> CVEID
T.intercalate CVEID
"\n\n" [CVEID]
descriptions

instance FromJSON CVE where
  parseJSON :: Value -> Parser CVE
parseJSON =
    String -> (Object -> Parser CVE) -> Value -> Parser CVE
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"CVE" ((Object -> Parser CVE) -> Value -> Parser CVE)
-> (Object -> Parser CVE) -> Value -> Parser CVE
forall a b. (a -> b) -> a -> b
$ \Object
o -> do
      Object
cve <- Object
o Object -> CVEID -> Parser Object
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"cve"
      Object
meta <- Object
cve Object -> CVEID -> Parser Object
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"CVE_data_meta"
      CVEID
cveID <- Object
meta Object -> CVEID -> Parser CVEID
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"ID"
      String -> Parser CVE -> Parser CVE
forall a. String -> Parser a -> Parser a
prependFailure (CVEID -> String
T.unpack CVEID
cveID String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
": ") (Parser CVE -> Parser CVE) -> Parser CVE -> Parser CVE
forall a b. (a -> b) -> a -> b
$ do
        Object
cfgs <- Object
o Object -> CVEID -> Parser Object
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"configurations"
        [CPEMatch]
cveCPEMatches <- Object -> Parser [CPEMatch]
parseConfigurations Object
cfgs
        UTCTime
cvePublished <- Object
o Object -> CVEID -> Parser UTCTime
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"publishedDate"
        UTCTime
cveLastModified <- Object
o Object -> CVEID -> Parser UTCTime
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"lastModifiedDate"
        Object
description <- Object
cve Object -> CVEID -> Parser Object
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"description"
        CVEID
cveDescription <- Object -> Parser CVEID
parseDescription Object
description
        CVE -> Parser CVE
forall (f :: * -> *) a. Applicative f => a -> f a
pure CVE :: CVEID -> [CPEMatch] -> CVEID -> UTCTime -> UTCTime -> CVE
CVE {[CPEMatch]
CVEID
UTCTime
cveDescription :: CVEID
cveLastModified :: UTCTime
cvePublished :: UTCTime
cveCPEMatches :: [CPEMatch]
cveID :: CVEID
cveLastModified :: UTCTime
cvePublished :: UTCTime
cveDescription :: CVEID
cveCPEMatches :: [CPEMatch]
cveID :: CVEID
..}

instance ToRow CVE where
  toRow :: CVE -> [SQLData]
toRow CVE {CVEID
cveID :: CVEID
cveID :: CVE -> CVEID
cveID, CVEID
cveDescription :: CVEID
cveDescription :: CVE -> CVEID
cveDescription, UTCTime
cvePublished :: UTCTime
cvePublished :: CVE -> UTCTime
cvePublished, UTCTime
cveLastModified :: UTCTime
cveLastModified :: CVE -> UTCTime
cveLastModified} =
    (CVEID, CVEID, UTCTime, UTCTime) -> [SQLData]
forall a. ToRow a => a -> [SQLData]
toRow (CVEID
cveID, CVEID
cveDescription, UTCTime
cvePublished, UTCTime
cveLastModified)

instance FromRow CVE where
  fromRow :: RowParser CVE
fromRow = do
    let cveCPEMatches :: [a]
cveCPEMatches = []
    CVEID
cveID <- RowParser CVEID
forall a. FromField a => RowParser a
field
    CVEID
cveDescription <- RowParser CVEID
forall a. FromField a => RowParser a
field
    UTCTime
cvePublished <- RowParser UTCTime
forall a. FromField a => RowParser a
field
    UTCTime
cveLastModified <- RowParser UTCTime
forall a. FromField a => RowParser a
field
    CVE -> RowParser CVE
forall (f :: * -> *) a. Applicative f => a -> f a
pure CVE :: CVEID -> [CPEMatch] -> CVEID -> UTCTime -> UTCTime -> CVE
CVE {[CPEMatch]
CVEID
UTCTime
forall a. [a]
cveLastModified :: UTCTime
cvePublished :: UTCTime
cveDescription :: CVEID
cveID :: CVEID
cveCPEMatches :: forall a. [a]
cveLastModified :: UTCTime
cvePublished :: UTCTime
cveDescription :: CVEID
cveCPEMatches :: [CPEMatch]
cveID :: CVEID
..}

splitCPE :: Text -> [Maybe Text]
splitCPE :: CVEID -> [Maybe CVEID]
splitCPE =
  (CVEID -> Maybe CVEID) -> [CVEID] -> [Maybe CVEID]
forall a b. (a -> b) -> [a] -> [b]
map (CVEID -> Maybe CVEID
forall a. (Eq a, IsString a) => a -> Maybe a
toMaybe (CVEID -> Maybe CVEID) -> (CVEID -> CVEID) -> CVEID -> Maybe CVEID
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CVEID -> CVEID -> CVEID -> CVEID
T.replace CVEID
"\a" CVEID
":") ([CVEID] -> [Maybe CVEID])
-> (CVEID -> [CVEID]) -> CVEID -> [Maybe CVEID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CVEID -> CVEID -> [CVEID]
T.splitOn CVEID
":" (CVEID -> [CVEID]) -> (CVEID -> CVEID) -> CVEID -> [CVEID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CVEID -> CVEID -> CVEID -> CVEID
T.replace CVEID
"\\:" CVEID
"\a"
  where
    toMaybe :: a -> Maybe a
toMaybe a
"*" = Maybe a
forall a. Maybe a
Nothing
    toMaybe a
x = a -> Maybe a
forall a. a -> Maybe a
Just a
x

instance FromJSON CPEMatch where
  parseJSON :: Value -> Parser CPEMatch
parseJSON =
    String -> (Object -> Parser CPEMatch) -> Value -> Parser CPEMatch
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"CPEMatch" ((Object -> Parser CPEMatch) -> Value -> Parser CPEMatch)
-> (Object -> Parser CPEMatch) -> Value -> Parser CPEMatch
forall a b. (a -> b) -> a -> b
$ \Object
o -> do
      CVEID
t <- Object
o Object -> CVEID -> Parser CVEID
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"cpe23Uri"
      CPE
cpeMatchCPE <-
        case CVEID -> [Maybe CVEID]
splitCPE CVEID
t of
          [Just CVEID
"cpe", Just CVEID
"2.3", Maybe CVEID
cpePart, Maybe CVEID
cpeVendor, Maybe CVEID
cpeProduct, Maybe CVEID
cpeVersion, Maybe CVEID
cpeUpdate, Maybe CVEID
cpeEdition, Maybe CVEID
cpeLanguage, Maybe CVEID
cpeSoftwareEdition, Maybe CVEID
cpeTargetSoftware, Maybe CVEID
cpeTargetHardware, Maybe CVEID
cpeOther] ->
            CPE -> Parser CPE
forall (f :: * -> *) a. Applicative f => a -> f a
pure CPE :: Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> Maybe CVEID
-> CPE
CPE {Maybe CVEID
cpeOther :: Maybe CVEID
cpeTargetHardware :: Maybe CVEID
cpeTargetSoftware :: Maybe CVEID
cpeSoftwareEdition :: Maybe CVEID
cpeLanguage :: Maybe CVEID
cpeEdition :: Maybe CVEID
cpeUpdate :: Maybe CVEID
cpeVersion :: Maybe CVEID
cpeProduct :: Maybe CVEID
cpeVendor :: Maybe CVEID
cpePart :: Maybe CVEID
cpeOther :: Maybe CVEID
cpeTargetHardware :: Maybe CVEID
cpeTargetSoftware :: Maybe CVEID
cpeSoftwareEdition :: Maybe CVEID
cpeLanguage :: Maybe CVEID
cpeEdition :: Maybe CVEID
cpeUpdate :: Maybe CVEID
cpeVersion :: Maybe CVEID
cpeProduct :: Maybe CVEID
cpeVendor :: Maybe CVEID
cpePart :: Maybe CVEID
..}
          [Maybe CVEID]
_ -> String -> Parser CPE
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Parser CPE) -> String -> Parser CPE
forall a b. (a -> b) -> a -> b
$ String
"unparsable cpe23Uri: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> CVEID -> String
T.unpack CVEID
t
      Bool
cpeMatchVulnerable <- Object
o Object -> CVEID -> Parser Bool
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"vulnerable"
      Maybe CVEID
vStartIncluding <- Object
o Object -> CVEID -> Parser (Maybe CVEID)
forall a. FromJSON a => Object -> CVEID -> Parser (Maybe a)
.:! CVEID
"versionStartIncluding"
      Maybe CVEID
vEndIncluding <- Object
o Object -> CVEID -> Parser (Maybe CVEID)
forall a. FromJSON a => Object -> CVEID -> Parser (Maybe a)
.:! CVEID
"versionEndIncluding"
      Maybe CVEID
vStartExcluding <- Object
o Object -> CVEID -> Parser (Maybe CVEID)
forall a. FromJSON a => Object -> CVEID -> Parser (Maybe a)
.:! CVEID
"versionStartExcluding"
      Maybe CVEID
vEndExcluding <- Object
o Object -> CVEID -> Parser (Maybe CVEID)
forall a. FromJSON a => Object -> CVEID -> Parser (Maybe a)
.:! CVEID
"versionEndExcluding"
      Boundary CVEID
startBoundary <-
        case (Maybe CVEID
vStartIncluding, Maybe CVEID
vStartExcluding) of
          (Maybe CVEID
Nothing, Maybe CVEID
Nothing) -> Boundary CVEID -> Parser (Boundary CVEID)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Boundary CVEID
forall a. Boundary a
Unbounded
          (Just CVEID
start, Maybe CVEID
Nothing) -> Boundary CVEID -> Parser (Boundary CVEID)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CVEID -> Boundary CVEID
forall a. a -> Boundary a
Including CVEID
start)
          (Maybe CVEID
Nothing, Just CVEID
start) -> Boundary CVEID -> Parser (Boundary CVEID)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CVEID -> Boundary CVEID
forall a. a -> Boundary a
Excluding CVEID
start)
          (Just CVEID
_, Just CVEID
_) -> String -> Parser (Boundary CVEID)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"multiple version starts"
      Boundary CVEID
endBoundary <-
        case (Maybe CVEID
vEndIncluding, Maybe CVEID
vEndExcluding) of
          (Maybe CVEID
Nothing, Maybe CVEID
Nothing) -> Boundary CVEID -> Parser (Boundary CVEID)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Boundary CVEID
forall a. Boundary a
Unbounded
          (Just CVEID
end, Maybe CVEID
Nothing) -> Boundary CVEID -> Parser (Boundary CVEID)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CVEID -> Boundary CVEID
forall a. a -> Boundary a
Including CVEID
end)
          (Maybe CVEID
Nothing, Just CVEID
end) -> Boundary CVEID -> Parser (Boundary CVEID)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CVEID -> Boundary CVEID
forall a. a -> Boundary a
Excluding CVEID
end)
          (Just CVEID
_, Just CVEID
_) -> String -> Parser (Boundary CVEID)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"multiple version ends"
      VersionMatcher
cpeMatchVersionMatcher <-
        case (CPE -> Maybe CVEID
cpeVersion CPE
cpeMatchCPE, Boundary CVEID
startBoundary, Boundary CVEID
endBoundary) of
          (Just CVEID
v, Boundary CVEID
Unbounded, Boundary CVEID
Unbounded) -> VersionMatcher -> Parser VersionMatcher
forall (f :: * -> *) a. Applicative f => a -> f a
pure (VersionMatcher -> Parser VersionMatcher)
-> VersionMatcher -> Parser VersionMatcher
forall a b. (a -> b) -> a -> b
$ CVEID -> VersionMatcher
SingleMatcher CVEID
v
          (Maybe CVEID
Nothing, Boundary CVEID
start, Boundary CVEID
end) -> VersionMatcher -> Parser VersionMatcher
forall (f :: * -> *) a. Applicative f => a -> f a
pure (VersionMatcher -> Parser VersionMatcher)
-> VersionMatcher -> Parser VersionMatcher
forall a b. (a -> b) -> a -> b
$ Boundary CVEID -> Boundary CVEID -> VersionMatcher
RangeMatcher Boundary CVEID
start Boundary CVEID
end
          (Maybe CVEID, Boundary CVEID, Boundary CVEID)
_ ->
            String -> Parser VersionMatcher
forall (m :: * -> *) a. MonadFail m => String -> m a
fail
              ( String
"cpe_match has both version "
                  String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Maybe CVEID -> String
forall a. Show a => a -> String
show (CPE -> Maybe CVEID
cpeVersion CPE
cpeMatchCPE)
                  String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
" in cpe, and boundaries from "
                  String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Boundary CVEID -> String
forall a. Show a => a -> String
show Boundary CVEID
startBoundary
                  String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
" to "
                  String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Boundary CVEID -> String
forall a. Show a => a -> String
show Boundary CVEID
endBoundary
              )
      CPEMatch -> Parser CPEMatch
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CPEMatch :: CPE -> Bool -> VersionMatcher -> CPEMatch
CPEMatch {Bool
VersionMatcher
CPE
cpeMatchVersionMatcher :: VersionMatcher
cpeMatchVulnerable :: Bool
cpeMatchCPE :: CPE
cpeMatchVersionMatcher :: VersionMatcher
cpeMatchVulnerable :: Bool
cpeMatchCPE :: CPE
..})

data CPEMatchRow
  = CPEMatchRow CVE CPEMatch

instance ToRow CPEMatchRow where
  toRow :: CPEMatchRow -> [SQLData]
toRow (CPEMatchRow CVE {CVEID
cveID :: CVEID
cveID :: CVE -> CVEID
cveID} CPEMatch {CPE
cpeMatchCPE :: CPE
cpeMatchCPE :: CPEMatch -> CPE
cpeMatchCPE, VersionMatcher
cpeMatchVersionMatcher :: VersionMatcher
cpeMatchVersionMatcher :: CPEMatch -> VersionMatcher
cpeMatchVersionMatcher}) =
    [Maybe CVEID -> SQLData
forall a. ToField a => a -> SQLData
toField (Maybe CVEID -> SQLData) -> Maybe CVEID -> SQLData
forall a b. (a -> b) -> a -> b
$ CVEID -> Maybe CVEID
forall a. a -> Maybe a
Just CVEID
cveID]
      [SQLData] -> [SQLData] -> [SQLData]
forall a. [a] -> [a] -> [a]
++ CPE -> [SQLData]
forall a. ToRow a => a -> [SQLData]
toRow CPE
cpeMatchCPE
      [SQLData] -> [SQLData] -> [SQLData]
forall a. [a] -> [a] -> [a]
++ [VersionMatcher -> SQLData
forall a. ToField a => a -> SQLData
toField VersionMatcher
cpeMatchVersionMatcher]

instance FromRow CPEMatchRow where
  fromRow :: RowParser CPEMatchRow
fromRow = do
    let cveCPEMatches :: [a]
cveCPEMatches = []
    let cveDescription :: a
cveDescription = a
forall a. HasCallStack => a
undefined
    let cvePublished :: a
cvePublished = a
forall a. HasCallStack => a
undefined
    let cveLastModified :: a
cveLastModified = a
forall a. HasCallStack => a
undefined
    CVEID
cveID <- RowParser CVEID
forall a. FromField a => RowParser a
field
    CPEMatch
cpeM <- RowParser CPEMatch
forall a. FromRow a => RowParser a
fromRow
    CPEMatchRow -> RowParser CPEMatchRow
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CPEMatchRow -> RowParser CPEMatchRow)
-> CPEMatchRow -> RowParser CPEMatchRow
forall a b. (a -> b) -> a -> b
$ CVE -> CPEMatch -> CPEMatchRow
CPEMatchRow (CVE :: CVEID -> [CPEMatch] -> CVEID -> UTCTime -> UTCTime -> CVE
CVE {[CPEMatch]
CVEID
UTCTime
forall a. a
forall a. [a]
cveID :: CVEID
cveLastModified :: forall a. a
cvePublished :: forall a. a
cveDescription :: forall a. a
cveCPEMatches :: forall a. [a]
cveLastModified :: UTCTime
cvePublished :: UTCTime
cveDescription :: CVEID
cveCPEMatches :: [CPEMatch]
cveID :: CVEID
..}) CPEMatch
cpeM

cpeMatches :: [CVE] -> [CPEMatchRow]
cpeMatches :: [CVE] -> [CPEMatchRow]
cpeMatches = (CVE -> [CPEMatchRow]) -> [CVE] -> [CPEMatchRow]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap CVE -> [CPEMatchRow]
rows
  where
    rows :: CVE -> [CPEMatchRow]
rows CVE
cve = (CPEMatch -> CPEMatchRow) -> [CPEMatch] -> [CPEMatchRow]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (CVE -> CPEMatch -> CPEMatchRow
CPEMatchRow CVE
cve) (CVE -> [CPEMatch]
cveCPEMatches CVE
cve)

guardAttr :: (Eq a, FromJSON a, Show a) => Object -> Text -> a -> Parser ()
guardAttr :: Object -> CVEID -> a -> Parser ()
guardAttr Object
object CVEID
attribute a
expected = do
  a
actual <- Object
object Object -> CVEID -> Parser a
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
attribute
  Bool -> Parser () -> Parser ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (a
actual a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
expected) (Parser () -> Parser ()) -> Parser () -> Parser ()
forall a b. (a -> b) -> a -> b
$
    String -> Parser ()
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Parser ()) -> String -> Parser ()
forall a b. (a -> b) -> a -> b
$
      String
"unexpected "
        String -> ShowS
forall a. Semigroup a => a -> a -> a
<> CVEID -> String
T.unpack CVEID
attribute
        String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
", expected "
        String -> ShowS
forall a. Semigroup a => a -> a -> a
<> a -> String
forall a. Show a => a -> String
show a
expected
        String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
", got "
        String -> ShowS
forall a. Semigroup a => a -> a -> a
<> a -> String
forall a. Show a => a -> String
show a
actual

boundedMatcher :: VersionMatcher -> Bool
boundedMatcher :: VersionMatcher -> Bool
boundedMatcher (RangeMatcher Boundary CVEID
Unbounded Boundary CVEID
Unbounded) = Bool
False
boundedMatcher VersionMatcher
_ = Bool
True

-- Because complex boolean formulas can't be used to determine if a single
-- product/version is vulnerable, we simply use all leaves marked vulnerable.
parseNode :: Object -> Parser [CPEMatch]
parseNode :: Object -> Parser [CPEMatch]
parseNode Object
node = do
  Maybe [Object]
maybeChildren <- Object
node Object -> CVEID -> Parser (Maybe [Object])
forall a. FromJSON a => Object -> CVEID -> Parser (Maybe a)
.:! CVEID
"children"
  case Maybe [Object]
maybeChildren of
    Maybe [Object]
Nothing -> do
      [CPEMatch]
matches <- Object
node Object -> CVEID -> Parser (Maybe [CPEMatch])
forall a. FromJSON a => Object -> CVEID -> Parser (Maybe a)
.:! CVEID
"cpe_match" Parser (Maybe [CPEMatch]) -> [CPEMatch] -> Parser [CPEMatch]
forall a. Parser (Maybe a) -> a -> Parser a
.!= []
      [CPEMatch] -> Parser [CPEMatch]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([CPEMatch] -> Parser [CPEMatch])
-> [CPEMatch] -> Parser [CPEMatch]
forall a b. (a -> b) -> a -> b
$
        (CPEMatch -> Bool) -> [CPEMatch] -> [CPEMatch]
forall a. (a -> Bool) -> [a] -> [a]
filter (CPEMatch -> VersionMatcher
cpeMatchVersionMatcher (CPEMatch -> VersionMatcher)
-> (VersionMatcher -> Bool) -> CPEMatch -> Bool
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> VersionMatcher -> Bool
boundedMatcher) ([CPEMatch] -> [CPEMatch]) -> [CPEMatch] -> [CPEMatch]
forall a b. (a -> b) -> a -> b
$
          (CPEMatch -> Bool) -> [CPEMatch] -> [CPEMatch]
forall a. (a -> Bool) -> [a] -> [a]
filter CPEMatch -> Bool
cpeMatchVulnerable [CPEMatch]
matches
    Just [Object]
children -> do
      ([[CPEMatch]] -> [CPEMatch])
-> Parser [[CPEMatch]] -> Parser [CPEMatch]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [[CPEMatch]] -> [CPEMatch]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Parser [[CPEMatch]] -> Parser [CPEMatch])
-> Parser [[CPEMatch]] -> Parser [CPEMatch]
forall a b. (a -> b) -> a -> b
$ [Parser [CPEMatch]] -> Parser [[CPEMatch]]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence ([Parser [CPEMatch]] -> Parser [[CPEMatch]])
-> [Parser [CPEMatch]] -> Parser [[CPEMatch]]
forall a b. (a -> b) -> a -> b
$ (Object -> Parser [CPEMatch]) -> [Object] -> [Parser [CPEMatch]]
forall a b. (a -> b) -> [a] -> [b]
map Object -> Parser [CPEMatch]
parseNode [Object]
children

parseConfigurations :: Object -> Parser [CPEMatch]
parseConfigurations :: Object -> Parser [CPEMatch]
parseConfigurations Object
o = do
  Object -> CVEID -> CVEID -> Parser ()
forall a.
(Eq a, FromJSON a, Show a) =>
Object -> CVEID -> a -> Parser ()
guardAttr Object
o CVEID
"CVE_data_version" (CVEID
"4.0" :: Text)
  [Object]
nodes <- Object
o Object -> CVEID -> Parser [Object]
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"nodes"
  ([[CPEMatch]] -> [CPEMatch])
-> Parser [[CPEMatch]] -> Parser [CPEMatch]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [[CPEMatch]] -> [CPEMatch]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Parser [[CPEMatch]] -> Parser [CPEMatch])
-> Parser [[CPEMatch]] -> Parser [CPEMatch]
forall a b. (a -> b) -> a -> b
$ [Parser [CPEMatch]] -> Parser [[CPEMatch]]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence ([Parser [CPEMatch]] -> Parser [[CPEMatch]])
-> [Parser [CPEMatch]] -> Parser [[CPEMatch]]
forall a b. (a -> b) -> a -> b
$ (Object -> Parser [CPEMatch]) -> [Object] -> [Parser [CPEMatch]]
forall a b. (a -> b) -> [a] -> [b]
map Object -> Parser [CPEMatch]
parseNode [Object]
nodes

parseFeed :: BSL.ByteString -> Either Text [CVE]
parseFeed :: ByteString -> Either CVEID [CVE]
parseFeed = (String -> CVEID)
-> (CVEFeed -> [CVE])
-> Either String CVEFeed
-> Either CVEID [CVE]
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap String -> CVEID
T.pack CVEFeed -> [CVE]
cvefItems (Either String CVEFeed -> Either CVEID [CVE])
-> (ByteString -> Either String CVEFeed)
-> ByteString
-> Either CVEID [CVE]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String CVEFeed
forall a. FromJSON a => ByteString -> Either String a
eitherDecode

data CVEFeed = CVEFeed
  { CVEFeed -> [CVE]
cvefItems :: [CVE]
  }

instance FromJSON CVEFeed where
  parseJSON :: Value -> Parser CVEFeed
parseJSON = String -> (Object -> Parser CVEFeed) -> Value -> Parser CVEFeed
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"CVEFeed" ((Object -> Parser CVEFeed) -> Value -> Parser CVEFeed)
-> (Object -> Parser CVEFeed) -> Value -> Parser CVEFeed
forall a b. (a -> b) -> a -> b
$ \Object
o -> [CVE] -> CVEFeed
CVEFeed ([CVE] -> CVEFeed) -> Parser [CVE] -> Parser CVEFeed
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> CVEID -> Parser [CVE]
forall a. FromJSON a => Object -> CVEID -> Parser a
.: CVEID
"CVE_Items"