{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}

module Ecluse.Pilot.Osv (
    OsvAdvisory (..),
    OsvAffected (..),
    OsvPackage (..),
    OsvRange (..),
    OsvEvent (..),
    OsvDatabaseSpecific (..),
    ExtractedOsv (..),
    extractFromAdvisory,
    osvExportUrl,
) where

import Data.Aeson (FromJSON (..), withObject, (.:), (.:?))
import Data.Text qualified as T

{- | An ecosystem's advisory export under an OSV-layout base URL
(@\<base\>\/\<ecosystem\>\/all.zip@): a zip archive of every advisory currently
published for the ecosystem. The base comes from configuration
(@osvExportBaseUrl@), so a moved or mirrored upstream never needs a new
binary; a trailing slash on the base is tolerated.

>>> osvExportUrl "https://osv-vulnerabilities.storage.googleapis.com/" "npm"
"https://osv-vulnerabilities.storage.googleapis.com/npm/all.zip"
-}
osvExportUrl :: Text -> Text -> String
osvExportUrl :: Text -> Text -> String
osvExportUrl Text
baseUrl Text
ecosystem =
    Text -> String
forall a. ToString a => a -> String
toString ((Char -> Bool) -> Text -> Text
T.dropWhileEnd (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'/') Text
baseUrl) String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"/" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a. ToString a => a -> String
toString Text
ecosystem String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"/all.zip"

-- | Exact model of what osv.dev makes available
data OsvAdvisory = OsvAdvisory
    { OsvAdvisory -> Text
osvId :: Text
    , OsvAdvisory -> Maybe [OsvAffected]
osvAffected :: Maybe [OsvAffected]
    , OsvAdvisory -> Maybe OsvDatabaseSpecific
osvDatabaseSpecific :: Maybe OsvDatabaseSpecific
    }
    deriving stock (Int -> OsvAdvisory -> String -> String
[OsvAdvisory] -> String -> String
OsvAdvisory -> String
(Int -> OsvAdvisory -> String -> String)
-> (OsvAdvisory -> String)
-> ([OsvAdvisory] -> String -> String)
-> Show OsvAdvisory
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> OsvAdvisory -> String -> String
showsPrec :: Int -> OsvAdvisory -> String -> String
$cshow :: OsvAdvisory -> String
show :: OsvAdvisory -> String
$cshowList :: [OsvAdvisory] -> String -> String
showList :: [OsvAdvisory] -> String -> String
Show, OsvAdvisory -> OsvAdvisory -> Bool
(OsvAdvisory -> OsvAdvisory -> Bool)
-> (OsvAdvisory -> OsvAdvisory -> Bool) -> Eq OsvAdvisory
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OsvAdvisory -> OsvAdvisory -> Bool
== :: OsvAdvisory -> OsvAdvisory -> Bool
$c/= :: OsvAdvisory -> OsvAdvisory -> Bool
/= :: OsvAdvisory -> OsvAdvisory -> Bool
Eq, (forall x. OsvAdvisory -> Rep OsvAdvisory x)
-> (forall x. Rep OsvAdvisory x -> OsvAdvisory)
-> Generic OsvAdvisory
forall x. Rep OsvAdvisory x -> OsvAdvisory
forall x. OsvAdvisory -> Rep OsvAdvisory x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. OsvAdvisory -> Rep OsvAdvisory x
from :: forall x. OsvAdvisory -> Rep OsvAdvisory x
$cto :: forall x. Rep OsvAdvisory x -> OsvAdvisory
to :: forall x. Rep OsvAdvisory x -> OsvAdvisory
Generic)

instance FromJSON OsvAdvisory where
    parseJSON :: Value -> Parser OsvAdvisory
parseJSON = String
-> (Object -> Parser OsvAdvisory) -> Value -> Parser OsvAdvisory
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"OsvAdvisory" ((Object -> Parser OsvAdvisory) -> Value -> Parser OsvAdvisory)
-> (Object -> Parser OsvAdvisory) -> Value -> Parser OsvAdvisory
forall a b. (a -> b) -> a -> b
$ \Object
v ->
        Text
-> Maybe [OsvAffected] -> Maybe OsvDatabaseSpecific -> OsvAdvisory
OsvAdvisory
            (Text
 -> Maybe [OsvAffected] -> Maybe OsvDatabaseSpecific -> OsvAdvisory)
-> Parser Text
-> Parser
     (Maybe [OsvAffected] -> Maybe OsvDatabaseSpecific -> OsvAdvisory)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"id"
            Parser
  (Maybe [OsvAffected] -> Maybe OsvDatabaseSpecific -> OsvAdvisory)
-> Parser (Maybe [OsvAffected])
-> Parser (Maybe OsvDatabaseSpecific -> OsvAdvisory)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser (Maybe [OsvAffected])
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"affected"
            Parser (Maybe OsvDatabaseSpecific -> OsvAdvisory)
-> Parser (Maybe OsvDatabaseSpecific) -> Parser OsvAdvisory
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser (Maybe OsvDatabaseSpecific)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"database_specific"

-- | The subset of an advisory's @database_specific@ block the pipeline consumes.
newtype OsvDatabaseSpecific = OsvDatabaseSpecific
    { OsvDatabaseSpecific -> Maybe Text
dbsSeverity :: Maybe Text
    {- ^ The source database's qualitative severity label (for GHSA-sourced npm
    advisories: @LOW@, @MODERATE@, @HIGH@, or @CRITICAL@).
    -}
    }
    deriving stock (Int -> OsvDatabaseSpecific -> String -> String
[OsvDatabaseSpecific] -> String -> String
OsvDatabaseSpecific -> String
(Int -> OsvDatabaseSpecific -> String -> String)
-> (OsvDatabaseSpecific -> String)
-> ([OsvDatabaseSpecific] -> String -> String)
-> Show OsvDatabaseSpecific
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> OsvDatabaseSpecific -> String -> String
showsPrec :: Int -> OsvDatabaseSpecific -> String -> String
$cshow :: OsvDatabaseSpecific -> String
show :: OsvDatabaseSpecific -> String
$cshowList :: [OsvDatabaseSpecific] -> String -> String
showList :: [OsvDatabaseSpecific] -> String -> String
Show, OsvDatabaseSpecific -> OsvDatabaseSpecific -> Bool
(OsvDatabaseSpecific -> OsvDatabaseSpecific -> Bool)
-> (OsvDatabaseSpecific -> OsvDatabaseSpecific -> Bool)
-> Eq OsvDatabaseSpecific
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OsvDatabaseSpecific -> OsvDatabaseSpecific -> Bool
== :: OsvDatabaseSpecific -> OsvDatabaseSpecific -> Bool
$c/= :: OsvDatabaseSpecific -> OsvDatabaseSpecific -> Bool
/= :: OsvDatabaseSpecific -> OsvDatabaseSpecific -> Bool
Eq, (forall x. OsvDatabaseSpecific -> Rep OsvDatabaseSpecific x)
-> (forall x. Rep OsvDatabaseSpecific x -> OsvDatabaseSpecific)
-> Generic OsvDatabaseSpecific
forall x. Rep OsvDatabaseSpecific x -> OsvDatabaseSpecific
forall x. OsvDatabaseSpecific -> Rep OsvDatabaseSpecific x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. OsvDatabaseSpecific -> Rep OsvDatabaseSpecific x
from :: forall x. OsvDatabaseSpecific -> Rep OsvDatabaseSpecific x
$cto :: forall x. Rep OsvDatabaseSpecific x -> OsvDatabaseSpecific
to :: forall x. Rep OsvDatabaseSpecific x -> OsvDatabaseSpecific
Generic)

instance FromJSON OsvDatabaseSpecific where
    parseJSON :: Value -> Parser OsvDatabaseSpecific
parseJSON = String
-> (Object -> Parser OsvDatabaseSpecific)
-> Value
-> Parser OsvDatabaseSpecific
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"OsvDatabaseSpecific" ((Object -> Parser OsvDatabaseSpecific)
 -> Value -> Parser OsvDatabaseSpecific)
-> (Object -> Parser OsvDatabaseSpecific)
-> Value
-> Parser OsvDatabaseSpecific
forall a b. (a -> b) -> a -> b
$ \Object
v ->
        Maybe Text -> OsvDatabaseSpecific
OsvDatabaseSpecific
            (Maybe Text -> OsvDatabaseSpecific)
-> Parser (Maybe Text) -> Parser OsvDatabaseSpecific
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"severity"

data OsvAffected = OsvAffected
    { OsvAffected -> OsvPackage
affectedPackage :: OsvPackage
    , OsvAffected -> Maybe [OsvRange]
affectedRanges :: Maybe [OsvRange]
    }
    deriving stock (Int -> OsvAffected -> String -> String
[OsvAffected] -> String -> String
OsvAffected -> String
(Int -> OsvAffected -> String -> String)
-> (OsvAffected -> String)
-> ([OsvAffected] -> String -> String)
-> Show OsvAffected
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> OsvAffected -> String -> String
showsPrec :: Int -> OsvAffected -> String -> String
$cshow :: OsvAffected -> String
show :: OsvAffected -> String
$cshowList :: [OsvAffected] -> String -> String
showList :: [OsvAffected] -> String -> String
Show, OsvAffected -> OsvAffected -> Bool
(OsvAffected -> OsvAffected -> Bool)
-> (OsvAffected -> OsvAffected -> Bool) -> Eq OsvAffected
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OsvAffected -> OsvAffected -> Bool
== :: OsvAffected -> OsvAffected -> Bool
$c/= :: OsvAffected -> OsvAffected -> Bool
/= :: OsvAffected -> OsvAffected -> Bool
Eq, (forall x. OsvAffected -> Rep OsvAffected x)
-> (forall x. Rep OsvAffected x -> OsvAffected)
-> Generic OsvAffected
forall x. Rep OsvAffected x -> OsvAffected
forall x. OsvAffected -> Rep OsvAffected x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. OsvAffected -> Rep OsvAffected x
from :: forall x. OsvAffected -> Rep OsvAffected x
$cto :: forall x. Rep OsvAffected x -> OsvAffected
to :: forall x. Rep OsvAffected x -> OsvAffected
Generic)

instance FromJSON OsvAffected where
    parseJSON :: Value -> Parser OsvAffected
parseJSON = String
-> (Object -> Parser OsvAffected) -> Value -> Parser OsvAffected
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"OsvAffected" ((Object -> Parser OsvAffected) -> Value -> Parser OsvAffected)
-> (Object -> Parser OsvAffected) -> Value -> Parser OsvAffected
forall a b. (a -> b) -> a -> b
$ \Object
v ->
        OsvPackage -> Maybe [OsvRange] -> OsvAffected
OsvAffected
            (OsvPackage -> Maybe [OsvRange] -> OsvAffected)
-> Parser OsvPackage -> Parser (Maybe [OsvRange] -> OsvAffected)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser OsvPackage
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"package"
            Parser (Maybe [OsvRange] -> OsvAffected)
-> Parser (Maybe [OsvRange]) -> Parser OsvAffected
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser (Maybe [OsvRange])
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"ranges"

data OsvPackage = OsvPackage
    { OsvPackage -> Text
packageName :: Text
    , OsvPackage -> Text
packageEcosystem :: Text
    }
    deriving stock (Int -> OsvPackage -> String -> String
[OsvPackage] -> String -> String
OsvPackage -> String
(Int -> OsvPackage -> String -> String)
-> (OsvPackage -> String)
-> ([OsvPackage] -> String -> String)
-> Show OsvPackage
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> OsvPackage -> String -> String
showsPrec :: Int -> OsvPackage -> String -> String
$cshow :: OsvPackage -> String
show :: OsvPackage -> String
$cshowList :: [OsvPackage] -> String -> String
showList :: [OsvPackage] -> String -> String
Show, OsvPackage -> OsvPackage -> Bool
(OsvPackage -> OsvPackage -> Bool)
-> (OsvPackage -> OsvPackage -> Bool) -> Eq OsvPackage
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OsvPackage -> OsvPackage -> Bool
== :: OsvPackage -> OsvPackage -> Bool
$c/= :: OsvPackage -> OsvPackage -> Bool
/= :: OsvPackage -> OsvPackage -> Bool
Eq, (forall x. OsvPackage -> Rep OsvPackage x)
-> (forall x. Rep OsvPackage x -> OsvPackage) -> Generic OsvPackage
forall x. Rep OsvPackage x -> OsvPackage
forall x. OsvPackage -> Rep OsvPackage x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. OsvPackage -> Rep OsvPackage x
from :: forall x. OsvPackage -> Rep OsvPackage x
$cto :: forall x. Rep OsvPackage x -> OsvPackage
to :: forall x. Rep OsvPackage x -> OsvPackage
Generic)

instance FromJSON OsvPackage where
    parseJSON :: Value -> Parser OsvPackage
parseJSON = String
-> (Object -> Parser OsvPackage) -> Value -> Parser OsvPackage
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"OsvPackage" ((Object -> Parser OsvPackage) -> Value -> Parser OsvPackage)
-> (Object -> Parser OsvPackage) -> Value -> Parser OsvPackage
forall a b. (a -> b) -> a -> b
$ \Object
v ->
        Text -> Text -> OsvPackage
OsvPackage
            (Text -> Text -> OsvPackage)
-> Parser Text -> Parser (Text -> OsvPackage)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"name"
            Parser (Text -> OsvPackage) -> Parser Text -> Parser OsvPackage
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"ecosystem"

data OsvRange = OsvRange
    { OsvRange -> Text
rangeType :: Text
    , OsvRange -> [OsvEvent]
rangeEvents :: [OsvEvent]
    }
    deriving stock (Int -> OsvRange -> String -> String
[OsvRange] -> String -> String
OsvRange -> String
(Int -> OsvRange -> String -> String)
-> (OsvRange -> String)
-> ([OsvRange] -> String -> String)
-> Show OsvRange
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> OsvRange -> String -> String
showsPrec :: Int -> OsvRange -> String -> String
$cshow :: OsvRange -> String
show :: OsvRange -> String
$cshowList :: [OsvRange] -> String -> String
showList :: [OsvRange] -> String -> String
Show, OsvRange -> OsvRange -> Bool
(OsvRange -> OsvRange -> Bool)
-> (OsvRange -> OsvRange -> Bool) -> Eq OsvRange
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OsvRange -> OsvRange -> Bool
== :: OsvRange -> OsvRange -> Bool
$c/= :: OsvRange -> OsvRange -> Bool
/= :: OsvRange -> OsvRange -> Bool
Eq, (forall x. OsvRange -> Rep OsvRange x)
-> (forall x. Rep OsvRange x -> OsvRange) -> Generic OsvRange
forall x. Rep OsvRange x -> OsvRange
forall x. OsvRange -> Rep OsvRange x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. OsvRange -> Rep OsvRange x
from :: forall x. OsvRange -> Rep OsvRange x
$cto :: forall x. Rep OsvRange x -> OsvRange
to :: forall x. Rep OsvRange x -> OsvRange
Generic)

instance FromJSON OsvRange where
    parseJSON :: Value -> Parser OsvRange
parseJSON = String -> (Object -> Parser OsvRange) -> Value -> Parser OsvRange
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"OsvRange" ((Object -> Parser OsvRange) -> Value -> Parser OsvRange)
-> (Object -> Parser OsvRange) -> Value -> Parser OsvRange
forall a b. (a -> b) -> a -> b
$ \Object
v ->
        Text -> [OsvEvent] -> OsvRange
OsvRange
            (Text -> [OsvEvent] -> OsvRange)
-> Parser Text -> Parser ([OsvEvent] -> OsvRange)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"type"
            Parser ([OsvEvent] -> OsvRange)
-> Parser [OsvEvent] -> Parser OsvRange
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser [OsvEvent]
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"events"

data OsvEvent = OsvEvent
    { OsvEvent -> Maybe Text
eventIntroduced :: Maybe Text
    , OsvEvent -> Maybe Text
eventFixed :: Maybe Text
    }
    deriving stock (Int -> OsvEvent -> String -> String
[OsvEvent] -> String -> String
OsvEvent -> String
(Int -> OsvEvent -> String -> String)
-> (OsvEvent -> String)
-> ([OsvEvent] -> String -> String)
-> Show OsvEvent
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> OsvEvent -> String -> String
showsPrec :: Int -> OsvEvent -> String -> String
$cshow :: OsvEvent -> String
show :: OsvEvent -> String
$cshowList :: [OsvEvent] -> String -> String
showList :: [OsvEvent] -> String -> String
Show, OsvEvent -> OsvEvent -> Bool
(OsvEvent -> OsvEvent -> Bool)
-> (OsvEvent -> OsvEvent -> Bool) -> Eq OsvEvent
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OsvEvent -> OsvEvent -> Bool
== :: OsvEvent -> OsvEvent -> Bool
$c/= :: OsvEvent -> OsvEvent -> Bool
/= :: OsvEvent -> OsvEvent -> Bool
Eq, (forall x. OsvEvent -> Rep OsvEvent x)
-> (forall x. Rep OsvEvent x -> OsvEvent) -> Generic OsvEvent
forall x. Rep OsvEvent x -> OsvEvent
forall x. OsvEvent -> Rep OsvEvent x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. OsvEvent -> Rep OsvEvent x
from :: forall x. OsvEvent -> Rep OsvEvent x
$cto :: forall x. Rep OsvEvent x -> OsvEvent
to :: forall x. Rep OsvEvent x -> OsvEvent
Generic)

instance FromJSON OsvEvent where
    parseJSON :: Value -> Parser OsvEvent
parseJSON = String -> (Object -> Parser OsvEvent) -> Value -> Parser OsvEvent
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"OsvEvent" ((Object -> Parser OsvEvent) -> Value -> Parser OsvEvent)
-> (Object -> Parser OsvEvent) -> Value -> Parser OsvEvent
forall a b. (a -> b) -> a -> b
$ \Object
v ->
        Maybe Text -> Maybe Text -> OsvEvent
OsvEvent
            (Maybe Text -> Maybe Text -> OsvEvent)
-> Parser (Maybe Text) -> Parser (Maybe Text -> OsvEvent)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"introduced"
            Parser (Maybe Text -> OsvEvent)
-> Parser (Maybe Text) -> Parser OsvEvent
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"fixed"

{- | The necessary subset of data requested by the goal:
package names, ecosystem identifiers, cve_id, and remediation boundaries.
-}
data ExtractedOsv = ExtractedOsv
    { ExtractedOsv -> Text
extPackage :: Text
    , ExtractedOsv -> Text
extEcosystem :: Text
    , ExtractedOsv -> Text
extCveId :: Text
    , ExtractedOsv -> Maybe Text
extIntroduced :: Maybe Text
    , ExtractedOsv -> Maybe Text
extFixed :: Maybe Text
    , ExtractedOsv -> Maybe Text
extSeverity :: Maybe Text
    {- ^ The advisory-level severity label, carried onto each of the advisory's
    ranges; 'Nothing' when the source database supplies none.
    -}
    }
    deriving stock (Int -> ExtractedOsv -> String -> String
[ExtractedOsv] -> String -> String
ExtractedOsv -> String
(Int -> ExtractedOsv -> String -> String)
-> (ExtractedOsv -> String)
-> ([ExtractedOsv] -> String -> String)
-> Show ExtractedOsv
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> ExtractedOsv -> String -> String
showsPrec :: Int -> ExtractedOsv -> String -> String
$cshow :: ExtractedOsv -> String
show :: ExtractedOsv -> String
$cshowList :: [ExtractedOsv] -> String -> String
showList :: [ExtractedOsv] -> String -> String
Show, ExtractedOsv -> ExtractedOsv -> Bool
(ExtractedOsv -> ExtractedOsv -> Bool)
-> (ExtractedOsv -> ExtractedOsv -> Bool) -> Eq ExtractedOsv
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ExtractedOsv -> ExtractedOsv -> Bool
== :: ExtractedOsv -> ExtractedOsv -> Bool
$c/= :: ExtractedOsv -> ExtractedOsv -> Bool
/= :: ExtractedOsv -> ExtractedOsv -> Bool
Eq)

extractFromAdvisory :: OsvAdvisory -> [ExtractedOsv]
extractFromAdvisory :: OsvAdvisory -> [ExtractedOsv]
extractFromAdvisory OsvAdvisory
adv = do
    aff <- [OsvAffected] -> Maybe [OsvAffected] -> [OsvAffected]
forall a. a -> Maybe a -> a
fromMaybe [] (OsvAdvisory -> Maybe [OsvAffected]
osvAffected OsvAdvisory
adv)
    let pkg = OsvAffected -> OsvPackage
affectedPackage OsvAffected
aff
    rng <- fromMaybe [] (affectedRanges aff)
    (intro, fixed) <- extractBounds (rangeEvents rng)
    pure $
        ExtractedOsv
            { extPackage = packageName pkg
            , extEcosystem = packageEcosystem pkg
            , extCveId = osvId adv
            , extIntroduced = intro
            , extFixed = fixed
            , extSeverity = dbsSeverity =<< osvDatabaseSpecific adv
            }

extractBounds :: [OsvEvent] -> [(Maybe Text, Maybe Text)]
extractBounds :: [OsvEvent] -> [(Maybe Text, Maybe Text)]
extractBounds = Maybe Text -> [OsvEvent] -> [(Maybe Text, Maybe Text)]
go Maybe Text
forall a. Maybe a
Nothing
  where
    go :: Maybe Text -> [OsvEvent] -> [(Maybe Text, Maybe Text)]
go (Just Text
i) [] = [(Text -> Maybe Text
forall a. a -> Maybe a
Just Text
i, Maybe Text
forall a. Maybe a
Nothing)]
    go Maybe Text
Nothing [] = []
    go Maybe Text
current (OsvEvent
e : [OsvEvent]
es)
        | Just Text
i <- OsvEvent -> Maybe Text
eventIntroduced OsvEvent
e = Maybe Text -> [OsvEvent] -> [(Maybe Text, Maybe Text)]
go (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
i) [OsvEvent]
es
        | Just Text
f <- OsvEvent -> Maybe Text
eventFixed OsvEvent
e = (Maybe Text
current, Text -> Maybe Text
forall a. a -> Maybe a
Just Text
f) (Maybe Text, Maybe Text)
-> [(Maybe Text, Maybe Text)] -> [(Maybe Text, Maybe Text)]
forall a. a -> [a] -> [a]
: Maybe Text -> [OsvEvent] -> [(Maybe Text, Maybe Text)]
go Maybe Text
forall a. Maybe a
Nothing [OsvEvent]
es
        | Bool
otherwise = Maybe Text -> [OsvEvent] -> [(Maybe Text, Maybe Text)]
go Maybe Text
current [OsvEvent]
es