{-# LANGUAGE OverloadedStrings #-}

module NVDRules where

import CVE (CPE (..), CPEMatch (..), CVE (..))
import Data.Char (isDigit)
import qualified Data.Text as T
import OurPrelude
import Text.Regex.Applicative.Text (RE', anySym, many, psym, (=~))
import Utils (Boundary (..), ProductID, Version, VersionMatcher (..))

-- Return False to discard CVE
filter :: CVE -> CPEMatch -> ProductID -> Version -> Bool
filter :: CVE -> CPEMatch -> ProductID -> ProductID -> Bool
filter CVE
_ CPEMatch
cpeMatch ProductID
"socat" ProductID
v
  | CPEMatch -> ProductID -> Bool
cpeUpdatePresentAndNotPartOfVersion CPEMatch
cpeMatch ProductID
v = Bool
False -- TODO consider if this rule should be applied to all packages
filter CVE
_ CPEMatch
cpeMatch ProductID
"uzbl" ProductID
v
  | Maybe () -> Bool
forall a. Maybe a -> Bool
isNothing (ProductID
v ProductID -> RE' () -> Maybe ()
forall a. ProductID -> RE' a -> Maybe a
=~ RE' ()
yearRegex)
      Bool -> Bool -> Bool
&& ProductID
"2009.12.22" ProductID -> VersionMatcher -> Bool
`anyVersionInfixOf` CPEMatch -> VersionMatcher
cpeMatchVersionMatcher CPEMatch
cpeMatch =
    Bool
False
  | Maybe () -> Bool
forall a. Maybe a -> Bool
isNothing (ProductID
v ProductID -> RE' () -> Maybe ()
forall a. ProductID -> RE' a -> Maybe a
=~ RE' ()
yearRegex)
      Bool -> Bool -> Bool
&& ProductID
"2010.04.03" ProductID -> VersionMatcher -> Bool
`anyVersionInfixOf` CPEMatch -> VersionMatcher
cpeMatchVersionMatcher CPEMatch
cpeMatch =
    Bool
False
filter CVE
_ CPEMatch
cpeMatch ProductID
"go" ProductID
v
  | ProductID
"." ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v
      Bool -> Bool -> Bool
&& ProductID
"-" ProductID -> VersionMatcher -> Bool
`anyVersionInfixOf` CPEMatch -> VersionMatcher
cpeMatchVersionMatcher CPEMatch
cpeMatch =
    Bool
False
filter CVE
_ CPEMatch
cpeMatch ProductID
"terraform" ProductID
_
  | CPE -> Maybe ProductID
cpeTargetSoftware (CPEMatch -> CPE
cpeMatchCPE CPEMatch
cpeMatch) Maybe ProductID -> Maybe ProductID -> Bool
forall a. Eq a => a -> a -> Bool
== ProductID -> Maybe ProductID
forall a. a -> Maybe a
Just ProductID
"aws" = Bool
False
filter CVE
cve CPEMatch
_ ProductID
"tor" ProductID
_
  | CVE -> ProductID
cveID CVE
cve ProductID -> ProductID -> Bool
forall a. Eq a => a -> a -> Bool
== ProductID
"CVE-2017-16541" = Bool
False
filter CVE
_ CPEMatch
cpeMatch ProductID
"arena" ProductID
_
  | CPE -> Maybe ProductID
cpeVendor (CPEMatch -> CPE
cpeMatchCPE CPEMatch
cpeMatch) Maybe ProductID -> Maybe ProductID -> Bool
forall a. Eq a => a -> a -> Bool
== ProductID -> Maybe ProductID
forall a. a -> Maybe a
Just ProductID
"rockwellautomation"
      Bool -> Bool -> Bool
|| CPE -> Maybe ProductID
cpeVendor (CPEMatch -> CPE
cpeMatchCPE CPEMatch
cpeMatch) Maybe ProductID -> Maybe ProductID -> Bool
forall a. Eq a => a -> a -> Bool
== ProductID -> Maybe ProductID
forall a. a -> Maybe a
Just ProductID
"openforis" =
    Bool
False
filter CVE
_ CPEMatch
cpeMatch ProductID
"thrift" ProductID
_
  | CPE -> Maybe ProductID
cpeVendor (CPEMatch -> CPE
cpeMatchCPE CPEMatch
cpeMatch) Maybe ProductID -> Maybe ProductID -> Bool
forall a. Eq a => a -> a -> Bool
== ProductID -> Maybe ProductID
forall a. a -> Maybe a
Just ProductID
"facebook" = Bool
False
filter CVE
_ CPEMatch
cpeMatch ProductID
"kanboard" ProductID
_
  | CPE -> Maybe ProductID
cpeTargetSoftware (CPEMatch -> CPE
cpeMatchCPE CPEMatch
cpeMatch) Maybe ProductID -> Maybe ProductID -> Bool
forall a. Eq a => a -> a -> Bool
== ProductID -> Maybe ProductID
forall a. a -> Maybe a
Just ProductID
"jenkins" = Bool
False
filter CVE
_cve CPEMatch
_match ProductID
_productID ProductID
_version = Bool
True

anyVersionInfixOf :: Text -> VersionMatcher -> Bool
anyVersionInfixOf :: ProductID -> VersionMatcher -> Bool
anyVersionInfixOf ProductID
t (SingleMatcher ProductID
v) = ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v
anyVersionInfixOf ProductID
t (RangeMatcher (Including ProductID
v1) (Including ProductID
v2)) =
  ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v1 Bool -> Bool -> Bool
|| ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v2
anyVersionInfixOf ProductID
t (RangeMatcher (Excluding ProductID
v1) (Excluding ProductID
v2)) =
  ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v1 Bool -> Bool -> Bool
|| ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v2
anyVersionInfixOf ProductID
t (RangeMatcher (Including ProductID
v1) (Excluding ProductID
v2)) =
  ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v1 Bool -> Bool -> Bool
|| ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v2
anyVersionInfixOf ProductID
t (RangeMatcher (Excluding ProductID
v1) (Including ProductID
v2)) =
  ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v1 Bool -> Bool -> Bool
|| ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v2
anyVersionInfixOf ProductID
t (RangeMatcher Boundary ProductID
Unbounded (Including ProductID
v)) = ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v
anyVersionInfixOf ProductID
t (RangeMatcher Boundary ProductID
Unbounded (Excluding ProductID
v)) = ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v
anyVersionInfixOf ProductID
t (RangeMatcher (Including ProductID
v) Boundary ProductID
Unbounded) = ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v
anyVersionInfixOf ProductID
t (RangeMatcher (Excluding ProductID
v) Boundary ProductID
Unbounded) = ProductID
t ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v
anyVersionInfixOf ProductID
_ (RangeMatcher Boundary ProductID
Unbounded Boundary ProductID
Unbounded) = Bool
False

-- Four digits at the start followed by any number of anything else
yearRegex :: RE' ()
yearRegex :: RE' ()
yearRegex =
  RE Char Char -> RE' ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (RE Char Char -> RE' ()) -> RE Char Char -> RE' ()
forall a b. (a -> b) -> a -> b
$
    (Char -> Bool) -> RE Char Char
psym Char -> Bool
isDigit RE Char Char -> RE Char Char -> RE Char Char
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* (Char -> Bool) -> RE Char Char
psym Char -> Bool
isDigit RE Char Char -> RE Char Char -> RE Char Char
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* (Char -> Bool) -> RE Char Char
psym Char -> Bool
isDigit RE Char Char -> RE Char Char -> RE Char Char
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* (Char -> Bool) -> RE Char Char
psym Char -> Bool
isDigit RE Char Char -> RE Char [Char] -> RE Char Char
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* RE Char Char -> RE Char [Char]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many RE Char Char
anySym

cpeUpdatePresentAndNotPartOfVersion :: CPEMatch -> Version -> Bool
cpeUpdatePresentAndNotPartOfVersion :: CPEMatch -> ProductID -> Bool
cpeUpdatePresentAndNotPartOfVersion CPEMatch
cpeMatch ProductID
v =
  Bool -> (ProductID -> Bool) -> Maybe ProductID -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
    Bool
False
    (\ProductID
update -> Bool -> Bool
not (ProductID
update ProductID -> ProductID -> Bool
`T.isInfixOf` ProductID
v))
    (CPE -> Maybe ProductID
cpeUpdate (CPEMatch -> CPE
cpeMatchCPE CPEMatch
cpeMatch))