From 8877d30fe869c1b89f8e3b905ba9af64747e286e Mon Sep 17 00:00:00 2001 From: Jess Lowe Date: Mon, 29 Sep 2025 00:18:31 +0000 Subject: [PATCH] Fix metrics being global causing concurrency issues --- vulnfeeds/cmd/cvelist2osv/converter.go | 36 ++++++++++----------- vulnfeeds/cmd/cvelist2osv/converter_test.go | 3 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/vulnfeeds/cmd/cvelist2osv/converter.go b/vulnfeeds/cmd/cvelist2osv/converter.go index c5934945053..8bdefc0dcf3 100644 --- a/vulnfeeds/cmd/cvelist2osv/converter.go +++ b/vulnfeeds/cmd/cvelist2osv/converter.go @@ -21,8 +21,8 @@ const ( extension = ".json" ) -// Metrics holds the collected data about the conversion process for a single CVE. -var Metrics struct { +// ConversionMetrics holds the collected data about the conversion process for a single CVE. +type ConversionMetrics struct { CNA string `json:"cna"` // The CNA that assigned the CVE. Outcome string `json:"outcome"` // The final outcome of the conversion (e.g., "Successful", "Failed"). Repos []string `json:"repos"` // A list of repositories extracted from the CVE's references. @@ -41,11 +41,11 @@ var RefTagDenyList = []string{ } // extractConversionMetrics examines a CVE and its generated OSV references to populate -// the global Metrics struct with heuristics about the conversion process. +// the ConversionMetrics struct with heuristics about the conversion process. // It captures the assigning CNA and counts the occurrences of each reference type. -func extractConversionMetrics(cve cves.CVE5, refs []osvschema.Reference) { +func extractConversionMetrics(cve cves.CVE5, refs []osvschema.Reference, metrics *ConversionMetrics) { // Capture the CNA for heuristic analysis. - Metrics.CNA = cve.Metadata.AssignerShortName + metrics.CNA = cve.Metadata.AssignerShortName // TODO(jesslowe): more CNA based analysis // Count number of references of each type @@ -53,9 +53,9 @@ func extractConversionMetrics(cve cves.CVE5, refs []osvschema.Reference) { for _, ref := range refs { refTypeCounts[ref.Type]++ } - Metrics.RefTypesCount = refTypeCounts + metrics.RefTypesCount = refTypeCounts for refType, count := range refTypeCounts { - Metrics.Notes = append(Metrics.Notes, fmt.Sprintf("[%s]: Reference Type %s: %d", cve.Metadata.CVEID, refType, count)) + metrics.Notes = append(metrics.Notes, fmt.Sprintf("[%s]: Reference Type %s: %d", cve.Metadata.CVEID, refType, count)) } // TODO(jesslowe): Add more analysis based on ADP containers, CVSS, KEV, CWE, etc. @@ -64,7 +64,7 @@ func extractConversionMetrics(cve cves.CVE5, refs []osvschema.Reference) { // FromCVE5 creates a `vulns.Vulnerability` object from a `cves.CVE5` object. // It populates the main fields of the OSV record, including ID, summary, details, // references, timestamps, severity, and version information. -func FromCVE5(cve cves.CVE5, refs []cves.Reference) (*vulns.Vulnerability, []string) { +func FromCVE5(cve cves.CVE5, refs []cves.Reference, metrics *ConversionMetrics) (*vulns.Vulnerability, []string) { aliases, related := vulns.ExtractReferencedVulns(cve.Metadata.CVEID, cve.Metadata.CVEID, refs) var notes []string v := vulns.Vulnerability{ @@ -95,7 +95,7 @@ func FromCVE5(cve cves.CVE5, refs []cves.Reference) (*vulns.Vulnerability, []str // Add affected version information. versionSources, versNotes := AddVersionInfo(cve, &v) notes = append(notes, versNotes...) - Metrics.VersionSources = versionSources + metrics.VersionSources = versionSources // TODO(jesslowe@): Add CWEs. // Combine severity metrics from both CNA and ADP containers. @@ -147,9 +147,9 @@ func writeOSVToFile(id cves.CVEID, cnaAssigner string, vulnDir string, v *vulns. // writeMetricToFile saves the collected conversion metrics to a JSON file. // This file provides data for analyzing the success and characteristics of the // conversion process for a given CVE. -func writeMetricToFile(id cves.CVEID, vulnDir string) error { +func writeMetricToFile(id cves.CVEID, vulnDir string, metrics *ConversionMetrics) error { metricsFile := filepath.Join(vulnDir, string(id)+".metrics.json") - marshalledMetrics, err := json.MarshalIndent(Metrics, "", " ") + marshalledMetrics, err := json.MarshalIndent(metrics, "", " ") if err != nil { logger.Warn("Failed to marshal metrics for "+string(id), slog.String("cve", string(id)), slog.Any("err", err)) return err @@ -168,18 +168,18 @@ func ConvertAndExportCVEToOSV(cve cves.CVE5, directory string) error { cveID := cve.Metadata.CVEID cnaAssigner := cve.Metadata.AssignerShortName references := identifyPossibleURLs(cve) - + metrics := &ConversionMetrics{} // Create a base OSV record from the CVE. - v, notes := FromCVE5(cve, references) - Metrics.Notes = append(Metrics.Notes, notes...) + v, notes := FromCVE5(cve, references, metrics) + metrics.Notes = append(metrics.Notes, notes...) // Collect metrics about the conversion. - extractConversionMetrics(cve, v.References) + extractConversionMetrics(cve, v.References, metrics) // Try to extract repository URLs from references. repos, repoNotes := cves.ReposFromReferencesCVEList(string(cveID), references, RefTagDenyList) - Metrics.Notes = append(Metrics.Notes, repoNotes...) - Metrics.Repos = repos + metrics.Notes = append(metrics.Notes, repoNotes...) + metrics.Repos = repos vulnDir := filepath.Join(directory, cnaAssigner) @@ -189,7 +189,7 @@ func ConvertAndExportCVEToOSV(cve cves.CVE5, directory string) error { } // Save the conversion metrics to a file. - if err := writeMetricToFile(cveID, vulnDir); err != nil { + if err := writeMetricToFile(cveID, vulnDir, metrics); err != nil { return err } diff --git a/vulnfeeds/cmd/cvelist2osv/converter_test.go b/vulnfeeds/cmd/cvelist2osv/converter_test.go index 990a3efbc16..9957f81a8cd 100644 --- a/vulnfeeds/cmd/cvelist2osv/converter_test.go +++ b/vulnfeeds/cmd/cvelist2osv/converter_test.go @@ -352,7 +352,8 @@ func TestFromCVE5(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - vuln, _ := FromCVE5(tc.cve, tc.refs) + metrics := &ConversionMetrics{} + vuln, _ := FromCVE5(tc.cve, tc.refs, metrics) // Handle non-deterministic time.Now() if strings.Contains(tc.name, "invalid date") {