From aabc6a04f3c0c44b5577f91e1bb32a8be1f40913 Mon Sep 17 00:00:00 2001 From: James Telfer <792299+jamestelfer@users.noreply.github.com> Date: Thu, 23 Nov 2023 09:18:39 +1100 Subject: [PATCH] feat: add CVSS3 scores to report CVSS3 scores are now shown preferentially, only falling back to CVSS2 when a CVSS3 score is not present. --- src/finding/summary.go | 35 ++++++++++++++++++- src/finding/summary_test.go | 28 +++++++++++++-- src/report/annotation.gohtml | 21 +++++++---- src/report/annotation_test.go | 10 ++++++ .../TestReports/findings_included.golden | 18 +++++++--- .../testdata/TestReports/image_label.golden | 2 ++ .../TestReports/no_vulnerabilities.golden | 2 ++ .../TestReports/some_findings_ignored.golden | 25 +++++++++---- 8 files changed, 120 insertions(+), 21 deletions(-) diff --git a/src/finding/summary.go b/src/finding/summary.go index 3f629d58..c0016e7a 100644 --- a/src/finding/summary.go +++ b/src/finding/summary.go @@ -2,6 +2,7 @@ package finding import ( "net/url" + "regexp" "slices" "strings" "time" @@ -26,6 +27,7 @@ type Detail struct { PackageName string PackageVersion string CVSS2 CVSSScore + CVSS3 CVSSScore Ignore *findingconfig.Ignore } @@ -132,6 +134,9 @@ func findingToDetail(finding types.ImageScanFinding) Detail { cvss2Vector := findingAttributeValue(finding, "CVSS2_VECTOR") cvss2VectorURL := cvss2VectorURL(cvss2Vector) + cvss3Vector := findingAttributeValue(finding, "CVSS3_VECTOR") + cvss3Vector, cvss3VectorURL := cvss3VectorURL(cvss3Vector) + return Detail{ Name: name, URI: uri, @@ -144,7 +149,11 @@ func findingToDetail(finding types.ImageScanFinding) Detail { Vector: cvss2Vector, VectorURL: cvss2VectorURL, }, - } + CVSS3: CVSSScore{ + Score: findingAttributeValue(finding, "CVSS3_SCORE"), + Vector: cvss3Vector, + VectorURL: cvss3VectorURL, + }} } func findingAttributeValue(finding types.ImageScanFinding, name string) string { @@ -184,3 +193,27 @@ func cvss2VectorURL(cvss2Vector string) string { } return cvss2VectorURL } + +// CVSS3 vector have their version at the front: we need to split this out to +// pass to the calculator URL +var cvss3VectorPattern = regexp.MustCompile(`^CVSS:([\d.]+)/(.+)$`) + +func cvss3VectorURL(versionedVector string) (string, string) { + vector := versionedVector + vectorURL := "" + + if versionedVector != "" { + version := "3.1" + + if matches := cvss3VectorPattern.FindStringSubmatch(versionedVector); matches != nil { + version = matches[1] + vector = matches[2] + } + + vectorURL = "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator" + + "?vector=" + url.QueryEscape(vector) + + "&version=" + url.QueryEscape(version) + } + + return vector, vectorURL +} diff --git a/src/finding/summary_test.go b/src/finding/summary_test.go index d3d6e1d7..8abb142d 100644 --- a/src/finding/summary_test.go +++ b/src/finding/summary_test.go @@ -64,18 +64,19 @@ func TestSummarize(t *testing.T) { }), }, { - name: "findings with CVSS2 scores", + name: "findings with CVSS2 and CVSS3 scores", data: types.ImageScanFindings{ Findings: []types.ImageScanFinding{ fscore("CVE-2019-5188", "HIGH", "1.2", "AV:L/AC:L/Au:N/C:P/I:P/A:P"), fscore("INVALID-CVE", "CRITICAL", "", ""), - fscore("CVE-2019-5189", "HIGH", "", ""), + fscore("CVE-2019-5189", "HIGH", "6", ""), + fscore3("CVE-2019-5189", "HIGH", "9", "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"), }, }, expected: autogold.Expect(finding.Summary{ Counts: map[types.FindingSeverity]finding.SeverityCount{ types.FindingSeverity("CRITICAL"): {Included: 1}, - types.FindingSeverity("HIGH"): {Included: 2}, + types.FindingSeverity("HIGH"): {Included: 3}, }, Details: []finding.Detail{ { @@ -94,6 +95,16 @@ func TestSummarize(t *testing.T) { { Name: "CVE-2019-5189", Severity: types.FindingSeverity("HIGH"), + CVSS2: finding.CVSSScore{Score: "6"}, + }, + { + Name: "CVE-2019-5189", + Severity: types.FindingSeverity("HIGH"), + CVSS3: finding.CVSSScore{ + Score: "9", + Vector: "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N", + VectorURL: "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV%3AN%2FAC%3AL%2FPR%3AN%2FUI%3AN%2FS%3AU%2FC%3AH%2FI%3AH%2FA%3AN&version=3.1", + }, }, }, Ignored: []finding.Detail{}, @@ -208,6 +219,17 @@ func fscore(name string, severity types.FindingSeverity, cvss2 string, vector st } } +func fscore3(name string, severity types.FindingSeverity, score string, vector string) types.ImageScanFinding { + return types.ImageScanFinding{ + Name: &name, + Severity: severity, + Attributes: []types.Attribute{ + {Key: aws.String("CVSS3_SCORE"), Value: &score}, + {Key: aws.String("CVSS3_VECTOR"), Value: &vector}, + }, + } +} + func i(id string) findingconfig.Ignore { return findingconfig.Ignore{ID: id} } diff --git a/src/report/annotation.gohtml b/src/report/annotation.gohtml index 4bebbe88..1ae1ee66 100644 --- a/src/report/annotation.gohtml +++ b/src/report/annotation.gohtml @@ -42,27 +42,33 @@ be wrapped in
tag by the Markdown renderer in Buildkite.
{{ define "findingName" }}{{ if .Description }}{{ template "findingNameLink" . }}
All listed scores are CVSS3 unless otherwise noted.
{{ if .FindingSummary.Details }}CVE | Severity | Affects | -CVSS2 score (vector) | +CVSS score | +CVSS vector |
---|---|---|---|---|---|
{{ template "findingName" . }} | {{ $f.Severity | string | lowerCase | titleCase }} | {{ $f.PackageName | nbsp }} {{ $f.PackageVersion | nbsp }} | -{{ template "cvssScore" $f.CVSS2 }} | +{{ template "cvssCells" $f }}
tag by the Markdown renderer in Buildkite.
tag by the Markdown renderer in Buildkite.
All listed scores are CVSS3 unless otherwise noted.
CVE | Severity | Affects | -CVSS2 score (vector) | +CVSS score | +CVSS vector |
---|---|---|---|---|---|
CVE-2019-5200Another vulnerability. |
Critical | 5200-package 5200-version | -10.0 (AV:L/AC:L/Au:N/C:P/I:P/A:P) | + +10.0 (*CVSS2) | AV:L/AC:L/Au:N/C:P/I:P/A:P | +
CVE-2019-5188A code execution vulnerability exists in the directory rehashing functionality of E2fsprogs e2fsck 1.45.4. A specially crafted ext4 directory can cause an out-of-bounds write on the stack, resulting in code execution. An attacker can corrupt a partition to trigger this vulnerability. |
High | e2fsprogs 1.44.1-1ubuntu1.1 | -4.6 (AV:L/AC:L/Au:N/C:P/I:P/A:P) | + +9 | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N | +
CVE-2019-5300Another vulnerability. |
Aa-Bogus-Severity | 5300-package 5300-version | -10.0 (AV:L/AC:L/Au:N/C:P/I:P/A:P) | + +10.0 (*CVSS2) | AV:L/AC:L/Au:N/C:P/I:P/A:P | +
scan completed: | source updated: diff --git a/src/report/testdata/TestReports/no_vulnerabilities.golden b/src/report/testdata/TestReports/no_vulnerabilities.golden index c56dd03c..f0c4ad78 100644 --- a/src/report/testdata/TestReports/no_vulnerabilities.golden +++ b/src/report/testdata/TestReports/no_vulnerabilities.golden @@ -14,6 +14,8 @@ + +
scan completed: |
source updated:
diff --git a/src/report/testdata/TestReports/some_findings_ignored.golden b/src/report/testdata/TestReports/some_findings_ignored.golden
index 586e4162..f1cdb937 100644
--- a/src/report/testdata/TestReports/some_findings_ignored.golden
+++ b/src/report/testdata/TestReports/some_findings_ignored.golden
@@ -45,30 +45,38 @@
+
+
All listed scores are CVSS3 unless otherwise noted.Vulnerability details
@@ -83,7 +91,8 @@
CVE
Severity
Affects
-CVSS2 score (vector)
+CVSS score
+CVSS vector
CVE-2019-5200
Critical
5200-package 5200-version
-10.0 (AV:L/AC:L/Au:N/C:P/I:P/A:P)
+
+10.0 (*CVSS2) AV:L/AC:L/Au:N/C:P/I:P/A:P
+
CVE-2019-5188
High
e2fsprogs 1.44.1-1ubuntu1.1
-4.6 (AV:L/AC:L/Au:N/C:P/I:P/A:P)
+
+4.6 (*CVSS2) AV:L/AC:L/Au:N/C:P/I:P/A:P
+
Severity
Ignored until
Affects
-CVSS2 score (vector)
+CVSS score
+CVSS vector
@@ -91,7 +100,9 @@
Critical
2023-12-31
5300-package 5300-version
-10.0 (AV:L/AC:L/Au:N/C:P/I:P/A:P)
+
+9 AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
+
@@ -99,7 +110,9 @@
Low
100-package 100-version
-4.0 (AV:L/AC:L/Au:N/C:P/I:P/A:P)
+
+4.0 (*CVSS2) AV:L/AC:L/Au:N/C:P/I:P/A:P
+