indicate if a CVE has been patched

This commit is contained in:
Ryan Mulligan 2019-10-28 07:41:45 -07:00
parent 78fe5fe745
commit 3741b7e634
4 changed files with 52 additions and 9 deletions

View File

@ -50,9 +50,15 @@ data CVE =
deriving (Show, Eq, Ord) deriving (Show, Eq, Ord)
-- | cve list item -- | cve list item
cveLI :: CVE -> Text cveLI :: CVE -> Bool -> Text
cveLI c = cveLI c patched =
"- [" <> cveID c <> "](https://nvd.nist.gov/vuln/detail/" <> cveID c <> ")" "- [" <>
cveID c <> "](https://nvd.nist.gov/vuln/detail/" <> cveID c <> ")" <> p
where
p =
if patched
then " (patched)"
else ""
-- This decodes an entire CPE string and related attributes, but we only use -- This decodes an entire CPE string and related attributes, but we only use
-- cpeVulnerable, cpeProduct, cpeVersion and cpeMatcher. -- cpeVulnerable, cpeProduct, cpeVersion and cpeMatcher.

View File

@ -117,9 +117,11 @@ main = do
Right t -> T.putStrLn t Right t -> T.putStrLn t
UpdateVulnDB -> withVulnDB $ \_conn -> pure () UpdateVulnDB -> withVulnDB $ \_conn -> pure ()
CheckAllVulnerable -> do CheckAllVulnerable -> do
setupNixpkgs undefined
updates <- T.readFile "packages-to-update.txt" updates <- T.readFile "packages-to-update.txt"
cveAll (Options undefined undefined) updates cveAll (Options undefined undefined) updates
CheckVulnerable productID oldVersion newVersion -> do CheckVulnerable productID oldVersion newVersion -> do
setupNixpkgs undefined
report <- report <-
cveReport cveReport
(UpdateEnv productID oldVersion newVersion (Options False undefined)) (UpdateEnv productID oldVersion newVersion (Options False undefined))

View File

@ -26,6 +26,9 @@ module Nix
, resultLink , resultLink
, sha256Zero , sha256Zero
, version , version
, getPatches
, hasPatchNamed
, Raw(..)
) where ) where
import OurPrelude import OurPrelude
@ -54,7 +57,7 @@ nixEvalET raw expr =
ourReadProcessInterleaved_ ourReadProcessInterleaved_
(proc "nix" (["eval", "-f", "."] <> rawOpt raw <> [T.unpack expr])) & (proc "nix" (["eval", "-f", "."] <> rawOpt raw <> [T.unpack expr])) &
fmapRT T.strip & fmapRT T.strip &
overwriteErrorT ("nix eval failed for " <> expr <> " ") overwriteErrorT ("nix eval failed for \"" <> expr <> "\"")
-- Error if the "new version" is actually newer according to nix -- Error if the "new version" is actually newer according to nix
assertNewerVersion :: MonadIO m => UpdateEnv -> ExceptT Text m () assertNewerVersion :: MonadIO m => UpdateEnv -> ExceptT Text m ()
@ -273,3 +276,15 @@ getHashFromBuild =
version :: MonadIO m => ExceptT Text m Text version :: MonadIO m => ExceptT Text m Text
version = ourReadProcessInterleaved_ "nix --version" version = ourReadProcessInterleaved_ "nix --version"
getPatches :: MonadIO m => Text -> ExceptT Text m Text
getPatches attrPath =
nixEvalET
NoRaw
("(let pkgs = import ./. {}; in (map (p: p.name) pkgs." <>
attrPath <> ".patches))")
hasPatchNamed :: MonadIO m => Text -> Text -> ExceptT Text m Bool
hasPatchNamed attrPath name = do
ps <- getPatches attrPath
return $ name `T.isInfixOf` ps

View File

@ -10,12 +10,13 @@ module Update
, cveReport , cveReport
, cveAll , cveAll
, sourceGithubAll , sourceGithubAll
, addPatched
) where ) where
import OurPrelude import OurPrelude
import qualified Blacklist import qualified Blacklist
import CVE (cveLI) import CVE (CVE, cveID, cveLI)
import qualified Check import qualified Check
import Control.Concurrent import Control.Concurrent
import qualified Data.ByteString.Lazy.Char8 as BSL import qualified Data.ByteString.Lazy.Char8 as BSL
@ -382,11 +383,27 @@ assertNotUpdatedOn updateEnv derivationFile branch = do
derivationContents <- fmapLT tshow $ tryIO $ T.readFile derivationFile derivationContents <- fmapLT tshow $ tryIO $ T.readFile derivationFile
Nix.assertOldVersionOn updateEnv branch derivationContents Nix.assertOldVersionOn updateEnv branch derivationContents
addPatched :: Text -> Set CVE -> IO [(CVE, Bool)]
addPatched attrPath set = do
let list = S.toList set
forM
list
(\cve -> do
patched <- runExceptT $ Nix.hasPatchNamed attrPath (cveID cve)
let p =
case patched of
Left _ -> False
Right r -> r
return (cve, p))
cveReport :: UpdateEnv -> IO Text cveReport :: UpdateEnv -> IO Text
cveReport updateEnv = cveReport updateEnv =
withVulnDB $ \conn withVulnDB $ \conn
-- TODO try other heuristics for project id -- TODO try other heuristics for project id
-- example false positive in current plan "vault" -- example false positive in current plan "vault"
-- def product_candidates(self):
-- return {self.pname, self.pname.replace('-', '_')}
-- TODO add fixup for Vendor
-> do -> do
oldCVEs <- oldCVEs <-
S.fromList <$> getCVEs conn (packageName updateEnv) (oldVersion updateEnv) S.fromList <$> getCVEs conn (packageName updateEnv) (oldVersion updateEnv)
@ -399,10 +416,13 @@ cveReport updateEnv =
if t == T.empty if t == T.empty
then "none" then "none"
else t else t
toMkdownList = S.toList >>> fmap cveLI >>> T.unlines >>> ifEmptyNone inOldButNotNew' <- addPatched (packageName updateEnv) inOldButNotNew
fixedList = toMkdownList inOldButNotNew inNewButNotOld' <- addPatched (packageName updateEnv) inNewButNotOld
newList = toMkdownList inNewButNotOld inBoth' <- addPatched (packageName updateEnv) inBoth
unresolvedList = toMkdownList inBoth let toMkdownList = fmap (uncurry cveLI) >>> T.unlines >>> ifEmptyNone
fixedList = toMkdownList inOldButNotNew'
newList = toMkdownList inNewButNotOld'
unresolvedList = toMkdownList inBoth'
if fixedList == "none" && unresolvedList == "none" && newList == "none" if fixedList == "none" && unresolvedList == "none" && newList == "none"
then return "" then return ""
else return else return