module Ecluse.Core.Cve (
CveDb (..),
openCveDb,
withCveDb,
CveLookup (..),
AdvisoryRange (..),
CveDbRejected (..),
insideAffectedRange,
) where
import UnliftIO.Exception (finally, onException)
import Ecluse.Core.Cve.Internal (AdvisoryRange (..), CveDbRejected (..), advisoriesQuery, openHardenedConnection, probeQuery, provenanceQuery)
import Ecluse.Core.Ecosystem (Ecosystem)
import Ecluse.Core.Version (compareVersions, mkVersion)
import Database.SQLite.Simple (Connection, close)
data CveLookup = CveLookup
{ CveLookup -> Text -> Text -> IO Bool
cveRemediationProbe :: Text -> Text -> IO Bool
, CveLookup -> Text -> IO [AdvisoryRange]
cveAdvisoriesFor :: Text -> IO [AdvisoryRange]
}
data CveDb = CveDb
{ CveDb -> CveLookup
cveDbLookup :: CveLookup
, CveDb -> IO ()
cveDbClose :: IO ()
, CveDb -> [(Text, Text)]
cveDbMeta :: [(Text, Text)]
}
openCveDb :: Ecosystem -> FilePath -> IO (Either CveDbRejected CveDb)
openCveDb :: Ecosystem -> FilePath -> IO (Either CveDbRejected CveDb)
openCveDb Ecosystem
eco FilePath
dbFile =
Ecosystem -> FilePath -> IO (Either CveDbRejected Connection)
openHardenedConnection Ecosystem
eco FilePath
dbFile IO (Either CveDbRejected Connection)
-> (Either CveDbRejected Connection
-> IO (Either CveDbRejected CveDb))
-> IO (Either CveDbRejected CveDb)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Left CveDbRejected
rejection -> Either CveDbRejected CveDb -> IO (Either CveDbRejected CveDb)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CveDbRejected -> Either CveDbRejected CveDb
forall a b. a -> Either a b
Left CveDbRejected
rejection)
Right Connection
conn -> do
meta <- Connection -> IO [(Text, Text)]
provenanceQuery Connection
conn IO [(Text, Text)] -> IO () -> IO [(Text, Text)]
forall (m :: * -> *) a b. MonadUnliftIO m => m a -> m b -> m a
`onException` Connection -> IO ()
close Connection
conn
pure (Right (mkCveDb conn meta))
mkCveDb :: Connection -> [(Text, Text)] -> CveDb
mkCveDb :: Connection -> [(Text, Text)] -> CveDb
mkCveDb Connection
conn [(Text, Text)]
meta =
CveDb
{ cveDbLookup :: CveLookup
cveDbLookup =
CveLookup
{ cveRemediationProbe :: Text -> Text -> IO Bool
cveRemediationProbe = Connection -> Text -> Text -> IO Bool
probeQuery Connection
conn
, cveAdvisoriesFor :: Text -> IO [AdvisoryRange]
cveAdvisoriesFor = Connection -> Text -> IO [AdvisoryRange]
advisoriesQuery Connection
conn
}
, cveDbClose :: IO ()
cveDbClose = Connection -> IO ()
close Connection
conn
, cveDbMeta :: [(Text, Text)]
cveDbMeta = [(Text, Text)]
meta
}
withCveDb :: Ecosystem -> FilePath -> (CveLookup -> IO a) -> IO (Either CveDbRejected a)
withCveDb :: forall a.
Ecosystem
-> FilePath -> (CveLookup -> IO a) -> IO (Either CveDbRejected a)
withCveDb Ecosystem
eco FilePath
dbFile CveLookup -> IO a
use =
Ecosystem -> FilePath -> IO (Either CveDbRejected CveDb)
openCveDb Ecosystem
eco FilePath
dbFile IO (Either CveDbRejected CveDb)
-> (Either CveDbRejected CveDb -> IO (Either CveDbRejected a))
-> IO (Either CveDbRejected a)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Left CveDbRejected
rejection -> Either CveDbRejected a -> IO (Either CveDbRejected a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CveDbRejected -> Either CveDbRejected a
forall a b. a -> Either a b
Left CveDbRejected
rejection)
Right CveDb
db -> a -> Either CveDbRejected a
forall a b. b -> Either a b
Right (a -> Either CveDbRejected a)
-> IO a -> IO (Either CveDbRejected a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (CveLookup -> IO a
use (CveDb -> CveLookup
cveDbLookup CveDb
db) IO a -> IO () -> IO a
forall (m :: * -> *) a b. MonadUnliftIO m => m a -> m b -> m a
`finally` CveDb -> IO ()
cveDbClose CveDb
db)
insideAffectedRange :: Ecosystem -> Text -> AdvisoryRange -> Bool
insideAffectedRange :: Ecosystem -> Text -> AdvisoryRange -> Bool
insideAffectedRange Ecosystem
eco Text
versionText AdvisoryRange
ar = Bool
atOrAboveIntroduced Bool -> Bool -> Bool
&& Bool
belowFixed
where
v :: Version
v = Ecosystem -> Text -> Version
mkVersion Ecosystem
eco Text
versionText
atOrAboveIntroduced :: Bool
atOrAboveIntroduced = case AdvisoryRange -> Maybe Text
arIntroduced AdvisoryRange
ar of
Maybe Text
Nothing -> Bool
True
Just Text
i -> case Version -> Version -> Maybe Ordering
compareVersions Version
v (Ecosystem -> Text -> Version
mkVersion Ecosystem
eco Text
i) of
Just Ordering
LT -> Bool
False
Just Ordering
_ -> Bool
True
Maybe Ordering
Nothing -> Bool
True
belowFixed :: Bool
belowFixed = case AdvisoryRange -> Maybe Text
arFixed AdvisoryRange
ar of
Maybe Text
Nothing -> Bool
True
Just Text
f -> case Version -> Version -> Maybe Ordering
compareVersions Version
v (Ecosystem -> Text -> Version
mkVersion Ecosystem
eco Text
f) of
Just Ordering
LT -> Bool
True
Just Ordering
_ -> Bool
False
Maybe Ordering
Nothing -> Bool
True