From 023f13cb669cdf1004eba1da17e557cb2ce714ee Mon Sep 17 00:00:00 2001 From: Conor Fitch <173908980+conorfitch@users.noreply.github.com> Date: Tue, 3 Feb 2026 01:59:12 +0000 Subject: [PATCH 1/2] add all cvss scores and sources for nvd Signed-off-by: Conor Fitch <173908980+conorfitch@users.noreply.github.com> --- vulnfeeds/cmd/combine-to-osv/main.go | 4 ++ vulnfeeds/cmd/converters/alpine/main.go | 2 +- vulnfeeds/cmd/converters/debian/main.go | 2 +- vulnfeeds/vulns/vulns.go | 56 +++++++++++++++++++------ 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/vulnfeeds/cmd/combine-to-osv/main.go b/vulnfeeds/cmd/combine-to-osv/main.go index ceae14c63a8..a55059ccf99 100644 --- a/vulnfeeds/cmd/combine-to-osv/main.go +++ b/vulnfeeds/cmd/combine-to-osv/main.go @@ -251,6 +251,10 @@ func combineTwoOSVRecords(cve5 *osvschema.Vulnerability, nvd *osvschema.Vulnerab } } + if len(nvd.GetSeverity()) > 0 { + baseOSV.Severity = nvd.GetSeverity() + } + return baseOSV } diff --git a/vulnfeeds/cmd/converters/alpine/main.go b/vulnfeeds/cmd/converters/alpine/main.go index adb57a3e3e8..31edc287660 100644 --- a/vulnfeeds/cmd/converters/alpine/main.go +++ b/vulnfeeds/cmd/converters/alpine/main.go @@ -202,7 +202,7 @@ func generateAlpineOSV(allAlpineSecDb map[string][]VersionAndPkg, allCVEs map[mo continue } if cve.CVE.Metrics != nil { - v.AddSeverity(cve.CVE.Metrics) + v.AddSingleSeverity(cve.CVE.Metrics) } osvVulnerabilities = append(osvVulnerabilities, v) diff --git a/vulnfeeds/cmd/converters/debian/main.go b/vulnfeeds/cmd/converters/debian/main.go index 6634f88c796..5375ed32b8c 100644 --- a/vulnfeeds/cmd/converters/debian/main.go +++ b/vulnfeeds/cmd/converters/debian/main.go @@ -123,7 +123,7 @@ func generateOSVFromDebianTracker(debianData DebianSecurityTrackerData, debianRe }, } if currentNVDCVE.CVE.Metrics != nil { - v.AddSeverity(currentNVDCVE.CVE.Metrics) + v.AddSingleSeverity(currentNVDCVE.CVE.Metrics) } osvCves[cveID] = v diff --git a/vulnfeeds/vulns/vulns.go b/vulnfeeds/vulns/vulns.go index 0fbdfccb174..0df6150cb0f 100644 --- a/vulnfeeds/vulns/vulns.go +++ b/vulnfeeds/vulns/vulns.go @@ -323,45 +323,77 @@ func (v *Vulnerability) AddPkgInfo(pkgInfo PackageInfo) { // getBestSeverity finds the best CVSS severity vector from the provided metrics data. // It prioritizes newer CVSS versions and "Primary" sources. -func getBestSeverity(metricsData *models.CVEItemMetrics) (string, string) { +func getBestSeverity(metricsData *models.CVEItemMetrics) (string, string, string) { + if metricsData == nil { + return "", "", "" + } // Define search passes. First pass for "Primary", second for any. for _, primaryOnly := range []bool{true, false} { // Inside each pass, prioritize v4.0 over v3.1 over v3.0. for _, metric := range metricsData.CVSSMetricV40 { if (!primaryOnly || metric.Type == "Primary") && metric.CVSSData.VectorString != "" { - return metric.CVSSData.VectorString, "CVSS_V4" + return metric.CVSSData.VectorString, "CVSS_V4", metric.Source } } for _, metric := range metricsData.CVSSMetricV31 { if (!primaryOnly || metric.Type == "Primary") && metric.CVSSData.VectorString != "" { - return metric.CVSSData.VectorString, "CVSS_V3" + return metric.CVSSData.VectorString, "CVSS_V3", metric.Source } } for _, metric := range metricsData.CVSSMetricV30 { if (!primaryOnly || metric.Type == "Primary") && metric.CVSSData.VectorString != "" { - return metric.CVSSData.VectorString, "CVSS_V3" + return metric.CVSSData.VectorString, "CVSS_V3", metric.Source } } } - return "", "" + return "", "", "" } -// AddSeverity adds CVSS severity information to the OSV vulnerability object. -// It uses the highest available CVSS score from the underlying CVE record. -func (v *Vulnerability) AddSeverity(metricsData *models.CVEItemMetrics) { - bestVectorString, severityType := getBestSeverity(metricsData) +// AddSingleSeverity adds CVSS severity information to the OSV vulnerability object. +// It uses the getBestSeverity function to determine the best severity. +func (v *Vulnerability) AddSingleSeverity(metricsData *models.CVEItemMetrics) { + bestVectorString, severityType, source := getBestSeverity(metricsData) if bestVectorString == "" { return } v.Severity = append(v.Severity, &osvschema.Severity{ - Type: osvschema.Severity_Type(osvschema.Severity_Type_value[severityType]), - Score: bestVectorString, + Type: osvschema.Severity_Type(osvschema.Severity_Type_value[severityType]), + Score: bestVectorString, + Source: source, }) } +// AddAllSeverities adds all the CVSS severity information from NVD to the OSV vulnerability object. +func (v *Vulnerability) AddAllSeverities(metricsData *models.CVEItemMetrics) { + if metricsData == nil { + return + } + + addSeverity := func(severityType osvschema.Severity_Type, vectorString, source string) { + if vectorString == "" { + return + } + v.Severity = append(v.Severity, &osvschema.Severity{ + Type: severityType, + Score: vectorString, + Source: source, + }) + } + + for _, metric := range metricsData.CVSSMetricV40 { + addSeverity(osvschema.Severity_CVSS_V4, metric.CVSSData.VectorString, metric.Source) + } + for _, metric := range metricsData.CVSSMetricV31 { + addSeverity(osvschema.Severity_CVSS_V3, metric.CVSSData.VectorString, metric.Source) + } + for _, metric := range metricsData.CVSSMetricV30 { + addSeverity(osvschema.Severity_CVSS_V3, metric.CVSSData.VectorString, metric.Source) + } +} + // ToJSON serializes the Vulnerability to JSON. func (v *Vulnerability) ToJSON(w io.Writer) error { unstableJSON, err := protojson.Marshal(v.Vulnerability) @@ -717,7 +749,7 @@ func FromNVDCVE(id models.CVEID, cve models.NVDCVE) *Vulnerability { References: ClassifyReferences(cve.References), }, } - v.AddSeverity(cve.Metrics) + v.AddAllSeverities(cve.Metrics) return v } From bcc1f48a9af096946a7cf4c680d7e1c1a1a807dc Mon Sep 17 00:00:00 2001 From: Conor Fitch <173908980+conorfitch@users.noreply.github.com> Date: Tue, 3 Feb 2026 01:59:24 +0000 Subject: [PATCH 2/2] add tests for new cvss scores Signed-off-by: Conor Fitch <173908980+conorfitch@users.noreply.github.com> --- vulnfeeds/cmd/combine-to-osv/main_test.go | 18 ++++++++++++++ vulnfeeds/cmd/converters/debian/main_test.go | 4 +-- vulnfeeds/vulns/vulns_test.go | 26 +++++++++++++++++--- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/vulnfeeds/cmd/combine-to-osv/main_test.go b/vulnfeeds/cmd/combine-to-osv/main_test.go index 6930851b895..2815a4c828b 100644 --- a/vulnfeeds/cmd/combine-to-osv/main_test.go +++ b/vulnfeeds/cmd/combine-to-osv/main_test.go @@ -379,6 +379,12 @@ func TestCombineTwoOSVRecords(t *testing.T) { Package: &osvschema.Package{Name: "package-a"}, }, }, + Severity: []*osvschema.Severity{ + { + Type: osvschema.Severity_CVSS_V3, + Score: "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N", + }, + }, } nvd := &osvschema.Vulnerability{ @@ -398,6 +404,12 @@ func TestCombineTwoOSVRecords(t *testing.T) { Package: &osvschema.Package{Name: "package-b"}, }, }, + Severity: []*osvschema.Severity{ + { + Type: osvschema.Severity_CVSS_V3, + Score: "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N", + }, + }, } expected := &osvschema.Vulnerability{ @@ -411,6 +423,12 @@ func TestCombineTwoOSVRecords(t *testing.T) { }, // pickAffectedInformation prefers nvd if it has more packages Affected: nvd.GetAffected(), + Severity: []*osvschema.Severity{ // Should take severity from NVD + { + Type: osvschema.Severity_CVSS_V3, + Score: "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N", + }, + }, } got := combineTwoOSVRecords(cve5, nvd) diff --git a/vulnfeeds/cmd/converters/debian/main_test.go b/vulnfeeds/cmd/converters/debian/main_test.go index 6635ea5e324..a9112bf0da1 100644 --- a/vulnfeeds/cmd/converters/debian/main_test.go +++ b/vulnfeeds/cmd/converters/debian/main_test.go @@ -143,7 +143,7 @@ func TestGenerateOSVFromDebianTracker(t *testing.T) { }, }, References: []*osvschema.Reference{{Type: osvschema.Reference_ADVISORY, Url: "https://security-tracker.debian.org/tracker/CVE-2016-1585"}}, - Severity: []*osvschema.Severity{{Type: osvschema.Severity_CVSS_V3, Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}}, + Severity: []*osvschema.Severity{{Type: osvschema.Severity_CVSS_V3, Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", Source: "nvd@nist.gov"}}, }, }, "CVE-2017-6507": { @@ -192,7 +192,7 @@ func TestGenerateOSVFromDebianTracker(t *testing.T) { }, }, References: []*osvschema.Reference{{Type: osvschema.Reference_ADVISORY, Url: "https://security-tracker.debian.org/tracker/CVE-2017-6507"}}, - Severity: []*osvschema.Severity{{Type: osvschema.Severity_CVSS_V3, Score: "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N"}}, + Severity: []*osvschema.Severity{{Type: osvschema.Severity_CVSS_V3, Score: "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N", Source: "nvd@nist.gov"}}, }, }, } diff --git a/vulnfeeds/vulns/vulns_test.go b/vulnfeeds/vulns/vulns_test.go index e5c84aa1586..6940e697e43 100644 --- a/vulnfeeds/vulns/vulns_test.go +++ b/vulnfeeds/vulns/vulns_test.go @@ -414,8 +414,9 @@ func TestAddSeverity(t *testing.T) { inputCVE: loadTestData2("CVE-2022-34668"), expectedResult: []*osvschema.Severity{ { - Type: osvschema.Severity_CVSS_V3, - Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + Type: osvschema.Severity_CVSS_V3, + Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + Source: "psirt@nvidia.com", }, }, }, @@ -424,8 +425,25 @@ func TestAddSeverity(t *testing.T) { inputCVE: loadTestData2("CVE-2023-5341"), expectedResult: []*osvschema.Severity{ { - Type: osvschema.Severity_CVSS_V3, - Score: "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + Type: osvschema.Severity_CVSS_V3, + Score: "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + Source: "secalert@redhat.com", + }, + }, + }, + { + description: "CVE with Primary and Secondary CVSS information", + inputCVE: loadTestData2("CVE-2022-36037"), + expectedResult: []*osvschema.Severity{ + { + Type: osvschema.Severity_CVSS_V3, + Score: "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N", + Source: "nvd@nist.gov", + }, + { + Type: osvschema.Severity_CVSS_V3, + Score: "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N", + Source: "security-advisories@github.com", }, }, },