Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions vulnfeeds/cmd/cvelist2osv/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -41,21 +41,21 @@ 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
refTypeCounts := make(map[osvschema.ReferenceType]int)
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.
Expand All @@ -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{
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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
}

Expand Down
3 changes: 2 additions & 1 deletion vulnfeeds/cmd/cvelist2osv/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
Loading