Skip to content

Commit 547fc4c

Browse files
TristanCacquerayblackheaven
authored andcommitted
Add initial CVSS improvements
1 parent d001ecb commit 547fc4c

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

code/cvss/src/Security/CVSS.hs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
-}
88
module Security.CVSS (
99
-- * Type
10-
CVSS,
10+
CVSS (cvssVersion),
11+
CVSSVersion (..),
1112
Rating (..),
1213
parseCVSS,
1314

1415
-- * Helpers
16+
cvssVectorString,
17+
cvssVectorStringOrdered,
1518
cvssScore,
1619
cvssInfo,
1720
) where
@@ -23,25 +26,33 @@ import Data.Text (Text)
2326
import Data.Text qualified as Text
2427
import GHC.Float (powerFloat)
2528

26-
data CVSSVersion = CVSS31
29+
-- | The CVSS version.
30+
data CVSSVersion
31+
= -- | Version 3.1: https://www.first.org/cvss/v3-1/
32+
CVSS31
2733

28-
-- | Parsed CVSS string obtained with 'parseCVSS'
34+
-- | Parsed CVSS string obtained with 'parseCVSS'.
2935
data CVSS = CVSS
3036
{ cvssVersion :: CVSSVersion
37+
-- ^ The CVSS Version.
3138
, cvssMetrics :: [Metric]
3239
-- ^ The metrics are stored as provided by the user
3340
}
3441

42+
instance Show CVSS where
43+
show = Text.unpack . cvssVectorString
44+
3545
-- | CVSS Rating obtained with 'cvssScore'
3646
data Rating = None | Low | Medium | High | Critical
3747
deriving (Enum, Eq, Ord, Show)
3848

49+
-- | Implementation of Section 5. "Qualitative Severity Rating Scale"
3950
toRating :: Float -> Rating
4051
toRating score
4152
| score <= 0 = None
42-
| score <= 3.9 = Low
43-
| score <= 6.9 = Medium
44-
| score <= 8.9 = High
53+
| score < 4 = Low
54+
| score < 7 = Medium
55+
| score < 9 = High
4556
| otherwise = Critical
4657

4758
type Metric = (Text, Char)
@@ -77,6 +88,25 @@ cvssInfo :: CVSS -> [Text]
7788
cvssInfo cvss = case cvssVersion cvss of
7889
CVSS31 -> cvss31info (cvssMetrics cvss)
7990

91+
-- | Format the CVSS back to its original string.
92+
cvssVectorString :: CVSS -> Text
93+
cvssVectorString = cvssShow False
94+
95+
-- | Format the CVSS to the prefered ordered vector string.
96+
cvssVectorStringOrdered :: CVSS -> Text
97+
cvssVectorStringOrdered = cvssShow True
98+
99+
cvssShow :: Bool -> CVSS -> Text
100+
cvssShow ordered cvss = case cvssVersion cvss of
101+
CVSS31 -> Text.intercalate "/" ("CVSS:3.1" : map toComponent (cvss31Order (cvssMetrics cvss)))
102+
where
103+
toComponent (name, value) = Text.snoc (name <> ":") value
104+
cvss31Order xs
105+
| ordered = mapMaybe getMetric allMetrics
106+
| otherwise = xs
107+
where
108+
getMetric mi = find (\(name, _) -> miShortName mi == name) xs
109+
80110
-- | Description of a metric group.
81111
data MetricGroup = MetricGroup
82112
{ mgName :: Text
@@ -200,6 +230,7 @@ cvss31info = map showMetricInfo
200230
allMetrics :: [MetricInfo]
201231
allMetrics = concatMap mgMetrics cvss31
202232

233+
-- | Implementation of the Appendix A - "Floating Point Rounding"
203234
roundup :: Float -> Float
204235
roundup input
205236
| int_input `mod` 10000 == 0 = fromIntegral int_input / 100000

code/cvss/test/Spec.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ main = defaultMain $
1414
forM_ examples $ \(cvssString, score, rating) -> do
1515
case CVSS.parseCVSS cvssString of
1616
Left e -> assertFailure (unpack e)
17-
Right cvss -> CVSS.cvssScore cvss @?= (rating, score)
17+
Right cvss -> do
18+
CVSS.cvssScore cvss @?= (rating, score)
19+
CVSS.cvssVectorString cvss @?= cvssString
20+
CVSS.cvssVectorStringOrdered cvss @?= cvssString
1821

1922
examples :: [(Text, Float, CVSS.Rating)]
2023
examples =

0 commit comments

Comments
 (0)