mirror of
https://github.com/nix-community/nixpkgs-update.git
synced 2024-11-28 23:22:00 +03:00
add experimental CVE reporting
This commit is contained in:
parent
303aed0afc
commit
ab2b5a6b91
@ -7,6 +7,7 @@ module CVE
|
||||
, CVE(..)
|
||||
, CVEID
|
||||
, cveMatcherList
|
||||
, cveLI
|
||||
) where
|
||||
|
||||
import OurPrelude
|
||||
@ -46,7 +47,12 @@ data CVE =
|
||||
, cvePublished :: UTCTime
|
||||
, cveLastModified :: UTCTime
|
||||
}
|
||||
deriving (Show)
|
||||
deriving (Show, Eq, Ord)
|
||||
|
||||
-- | cve list item
|
||||
cveLI :: CVE -> Text
|
||||
cveLI c =
|
||||
"- [" <> cveID c <> "](https://nvd.nist.gov/vuln/detail/" <> cveID c <> ")"
|
||||
|
||||
-- This decodes an entire CPE string and related attributes, but we only use
|
||||
-- cpeVulnerable, cpeProduct, cpeVersion and cpeMatcher.
|
||||
|
31
src/Main.hs
31
src/Main.hs
@ -11,12 +11,12 @@ import Control.Applicative ((<**>))
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
import DeleteMerged (deleteDone)
|
||||
import NVD (getCVEs, withVulnDB)
|
||||
import NVD (withVulnDB)
|
||||
import qualified Nix
|
||||
import qualified Options.Applicative as O
|
||||
import System.Posix.Env (setEnv)
|
||||
import Update (updateAll)
|
||||
import Utils (Options(..), setupNixpkgs)
|
||||
import Update (cveAll, cveReport, updateAll)
|
||||
import Utils (Options(..), UpdateEnv(..), setupNixpkgs)
|
||||
|
||||
default (T.Text)
|
||||
|
||||
@ -30,7 +30,8 @@ data Command
|
||||
| DeleteDone
|
||||
| Version
|
||||
| UpdateVulnDB
|
||||
| CheckVulnerable Text Text
|
||||
| CheckAllVulnerable
|
||||
| CheckVulnerable Text Text Text
|
||||
|
||||
updateOptionsParser :: O.Parser Command
|
||||
updateOptionsParser =
|
||||
@ -64,12 +65,18 @@ commandParser =
|
||||
(O.progDesc "Updates the vulnerability database")) <>
|
||||
O.command
|
||||
"check-vulnerable"
|
||||
(O.info checkVulnerable (O.progDesc "checks if something is vulnerable")))
|
||||
(O.info checkVulnerable (O.progDesc "checks if something is vulnerable")) <>
|
||||
O.command
|
||||
"check-all-vulnerable"
|
||||
(O.info
|
||||
(pure CheckAllVulnerable)
|
||||
(O.progDesc "checks all packages to update for vulnerabilities")))
|
||||
|
||||
checkVulnerable :: O.Parser Command
|
||||
checkVulnerable =
|
||||
CheckVulnerable <$> O.strArgument (O.metavar "PRODUCT_ID") <*>
|
||||
O.strArgument (O.metavar "VERSION")
|
||||
O.strArgument (O.metavar "OLD_VERSION") <*>
|
||||
O.strArgument (O.metavar "NEW_VERSION")
|
||||
|
||||
programInfo :: O.ParserInfo Command
|
||||
programInfo =
|
||||
@ -105,7 +112,11 @@ main = do
|
||||
Left t -> T.putStrLn ("error:" <> t)
|
||||
Right t -> T.putStrLn t
|
||||
UpdateVulnDB -> withVulnDB $ \_conn -> pure ()
|
||||
CheckVulnerable productId version ->
|
||||
withVulnDB $ \conn -> do
|
||||
cves <- getCVEs conn productId version
|
||||
mapM_ print cves
|
||||
CheckAllVulnerable -> do
|
||||
updates <- T.readFile "packages-to-update.txt"
|
||||
cveAll (Options undefined undefined) updates
|
||||
CheckVulnerable productID oldVersion newVersion -> do
|
||||
report <-
|
||||
cveReport
|
||||
(UpdateEnv productID oldVersion newVersion (Options False undefined))
|
||||
T.putStrLn report
|
||||
|
@ -7,11 +7,14 @@
|
||||
|
||||
module Update
|
||||
( updateAll
|
||||
, cveReport
|
||||
, cveAll
|
||||
) where
|
||||
|
||||
import OurPrelude
|
||||
|
||||
import qualified Blacklist
|
||||
import CVE (cveLI)
|
||||
import qualified Check
|
||||
import Control.Concurrent
|
||||
import qualified Data.ByteString.Lazy.Char8 as BSL
|
||||
@ -23,6 +26,7 @@ import Data.Time.Clock (UTCTime, getCurrentTime)
|
||||
import qualified File
|
||||
import qualified GH
|
||||
import qualified Git
|
||||
import NVD (getCVEs, withVulnDB)
|
||||
import qualified Nix
|
||||
import Outpaths
|
||||
import Prelude hiding (log)
|
||||
@ -63,6 +67,17 @@ updateAll o updates = do
|
||||
liftIO $ newIORef (MergeBaseOutpathsInfo twoHoursAgo S.empty)
|
||||
updateLoop o log (parseUpdates updates) mergeBaseOutpathSet
|
||||
|
||||
cveAll :: Options -> Text -> IO ()
|
||||
cveAll o updates = do
|
||||
let u' = rights $ parseUpdates updates
|
||||
results <-
|
||||
mapM
|
||||
(\(p, oldV, newV) -> do
|
||||
r <- cveReport (UpdateEnv p oldV newV o)
|
||||
return $ p <> ": " <> oldV <> " -> " <> newV <> "\n" <> r)
|
||||
u'
|
||||
T.putStrLn (T.unlines results)
|
||||
|
||||
updateLoop ::
|
||||
MonadIO m
|
||||
=> Options
|
||||
@ -178,6 +193,7 @@ publishPackage log updateEnv oldSrcUrl newSrcUrl attrPath result opDiff = do
|
||||
Left msg -> pure msg
|
||||
d <- Nix.getDescription attrPath <|> return T.empty
|
||||
u <- Nix.getHomepage attrPath <|> return T.empty
|
||||
cveRep <- liftIO $ cveReport updateEnv
|
||||
let metaDescription =
|
||||
if d == T.empty
|
||||
then ""
|
||||
@ -225,7 +241,8 @@ publishPackage log updateEnv oldSrcUrl newSrcUrl attrPath result opDiff = do
|
||||
attrPath
|
||||
maintainersCc
|
||||
result
|
||||
(outpathReport opDiff))
|
||||
(outpathReport opDiff)
|
||||
cveRep)
|
||||
Git.cleanAndResetTo "master"
|
||||
|
||||
repologyUrl :: UpdateEnv -> Text
|
||||
@ -266,7 +283,8 @@ prMessage ::
|
||||
-> Text
|
||||
-> Text
|
||||
-> Text
|
||||
prMessage updateEnv isBroken metaDescription metaHomepage releaseUrlMessage compareUrlMessage resultCheckReport commitHash attrPath maintainersCc resultPath opReport =
|
||||
-> Text
|
||||
prMessage updateEnv isBroken metaDescription metaHomepage releaseUrlMessage compareUrlMessage resultCheckReport commitHash attrPath maintainersCc resultPath opReport cveRep =
|
||||
let brokenMsg = brokenWarning isBroken
|
||||
title = prTitle updateEnv attrPath
|
||||
repologyLink = repologyUrl updateEnv
|
||||
@ -327,6 +345,9 @@ prMessage updateEnv isBroken metaDescription metaHomepage releaseUrlMessage comp
|
||||
|
||||
</details>
|
||||
<br/>
|
||||
|
||||
$cveRep
|
||||
|
||||
$maintainersCc
|
||||
|]
|
||||
|
||||
@ -348,3 +369,47 @@ assertNotUpdatedOn updateEnv derivationFile branch = do
|
||||
Git.cleanAndResetTo branch
|
||||
derivationContents <- fmapLT tshow $ tryIO $ T.readFile derivationFile
|
||||
Nix.assertOldVersionOn updateEnv branch derivationContents
|
||||
|
||||
cveReport :: UpdateEnv -> IO Text
|
||||
cveReport updateEnv =
|
||||
withVulnDB $ \conn
|
||||
-- TODO try other heuristics for project id
|
||||
-- example false positive in current plan "vault"
|
||||
-> do
|
||||
oldCVEs <-
|
||||
S.fromList <$> getCVEs conn (packageName updateEnv) (oldVersion updateEnv)
|
||||
newCVEs <-
|
||||
S.fromList <$> getCVEs conn (packageName updateEnv) (newVersion updateEnv)
|
||||
let inOldButNotNew = S.difference oldCVEs newCVEs
|
||||
inNewButNotOld = S.difference newCVEs oldCVEs
|
||||
inBoth = S.intersection oldCVEs newCVEs
|
||||
ifEmptyNone t =
|
||||
if t == T.empty
|
||||
then "none"
|
||||
else t
|
||||
toMkdownList = S.toList >>> fmap cveLI >>> T.unlines >>> ifEmptyNone
|
||||
fixedList = toMkdownList inOldButNotNew
|
||||
newList = toMkdownList inNewButNotOld
|
||||
unresolvedList = toMkdownList inBoth
|
||||
if fixedList == "none" && unresolvedList == "none" && newList == "none"
|
||||
then return ""
|
||||
else return
|
||||
[interpolate|
|
||||
<details>
|
||||
<summary>
|
||||
Experimental: CVE security report (click to expand)
|
||||
</summary>
|
||||
|
||||
CVEs resolved by this update:
|
||||
$fixedList
|
||||
|
||||
CVEs introduced by this update:
|
||||
$newList
|
||||
|
||||
CVEs present in both versions:
|
||||
$unresolvedList
|
||||
|
||||
|
||||
</details>
|
||||
<br/>
|
||||
|]
|
||||
|
Loading…
Reference in New Issue
Block a user