{-# LANGUAGE OverloadedStrings #-}

module Security.Advisories.Convert.OSV
  ( convert
  )
  where

import qualified Data.Text as T
import Data.Time (zonedTimeToUTC)
import Data.Void
import Distribution.Pretty (prettyShow)

import Security.Advisories
import qualified Security.OSV as OSV

convert :: Advisory -> OSV.Model Void Void Void Void
convert :: Advisory -> Model Void Void Void Void
convert Advisory
adv =
  ( Text -> UTCTime -> Model Void Any Any Any
forall dbs aes adbs rdbs.
Text -> UTCTime -> Model dbs aes adbs rdbs
OSV.newModel'
    (String -> Text
T.pack (String -> Text) -> (HsecId -> String) -> HsecId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HsecId -> String
printHsecId (HsecId -> Text) -> HsecId -> Text
forall a b. (a -> b) -> a -> b
$ Advisory -> HsecId
advisoryId Advisory
adv)
    (ZonedTime -> UTCTime
zonedTimeToUTC (ZonedTime -> UTCTime) -> ZonedTime -> UTCTime
forall a b. (a -> b) -> a -> b
$ Advisory -> ZonedTime
advisoryModified Advisory
adv)
  )
  { OSV.modelPublished = Just $ zonedTimeToUTC (advisoryPublished adv)
  , OSV.modelAliases = advisoryAliases adv
  , OSV.modelRelated = advisoryRelated adv
  , OSV.modelSummary = Just $ advisorySummary adv
  , OSV.modelDetails = Just $ advisoryDetails adv
  , OSV.modelReferences = advisoryReferences adv
  , OSV.modelAffected = fmap mkAffected (advisoryAffected adv)
  }

mkAffected :: Affected -> OSV.Affected Void Void Void
mkAffected :: Affected -> Affected Void Void Void
mkAffected Affected
aff =
  OSV.Affected
    { $sel:affectedPackage:Affected :: Package
OSV.affectedPackage = Text -> Package
mkPackage (Affected -> Text
affectedPackage Affected
aff)
    , $sel:affectedRanges:Affected :: [Range Void]
OSV.affectedRanges = Range Void -> [Range Void]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Range Void -> [Range Void]) -> Range Void -> [Range Void]
forall a b. (a -> b) -> a -> b
$ [AffectedVersionRange] -> Range Void
mkRange (Affected -> [AffectedVersionRange]
affectedVersions Affected
aff)
    , $sel:affectedSeverity:Affected :: [Severity]
OSV.affectedSeverity = [CVSS -> Severity
OSV.Severity (Affected -> CVSS
affectedCVSS Affected
aff)]
    , $sel:affectedEcosystemSpecific:Affected :: Maybe Void
OSV.affectedEcosystemSpecific = Maybe Void
forall a. Maybe a
Nothing
    , $sel:affectedDatabaseSpecific:Affected :: Maybe Void
OSV.affectedDatabaseSpecific = Maybe Void
forall a. Maybe a
Nothing
    }

mkPackage :: T.Text -> OSV.Package
mkPackage :: Text -> Package
mkPackage Text
name = OSV.Package
  { $sel:packageName:Package :: Text
OSV.packageName = Text
name
  , $sel:packageEcosystem:Package :: Text
OSV.packageEcosystem = Text
"Hackage"
  , $sel:packagePurl:Package :: Maybe Text
OSV.packagePurl = Maybe Text
forall a. Maybe a
Nothing
  }

mkRange :: [AffectedVersionRange] -> OSV.Range Void
mkRange :: [AffectedVersionRange] -> Range Void
mkRange [AffectedVersionRange]
ranges =
    [Event Text] -> Maybe Void -> Range Void
forall dbSpecific.
[Event Text] -> Maybe dbSpecific -> Range dbSpecific
OSV.RangeEcosystem ((AffectedVersionRange -> [Event Text])
-> [AffectedVersionRange] -> [Event Text]
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap AffectedVersionRange -> [Event Text]
mkEvs [AffectedVersionRange]
ranges) Maybe Void
forall a. Maybe a
Nothing
  where
    mkEvs :: AffectedVersionRange -> [OSV.Event T.Text]
    mkEvs :: AffectedVersionRange -> [Event Text]
mkEvs AffectedVersionRange
range =
      Text -> Event Text
forall a. a -> Event a
OSV.EventIntroduced (String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Version -> String
forall a. Pretty a => a -> String
prettyShow (Version -> String) -> Version -> String
forall a b. (a -> b) -> a -> b
$ AffectedVersionRange -> Version
affectedVersionRangeIntroduced AffectedVersionRange
range)
      Event Text -> [Event Text] -> [Event Text]
forall a. a -> [a] -> [a]
: [Event Text]
-> (Version -> [Event Text]) -> Maybe Version -> [Event Text]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (Event Text -> [Event Text]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Event Text -> [Event Text])
-> (Version -> Event Text) -> Version -> [Event Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Event Text
forall a. a -> Event a
OSV.EventFixed (Text -> Event Text) -> (Version -> Text) -> Version -> Event Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> (Version -> String) -> Version -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Version -> String
forall a. Pretty a => a -> String
prettyShow) (AffectedVersionRange -> Maybe Version
affectedVersionRangeFixed AffectedVersionRange
range)