Skip to content

Commit b596148

Browse files
authored
refactor: use AddNotes and AddSource fields in CVEList conversion (#4080)
Tidy up the code a bit to use an AddNotes method to append notes and sources.
1 parent 1821a23 commit b596148

File tree

2 files changed

+48
-30
lines changed

2 files changed

+48
-30
lines changed

vulnfeeds/cmd/cvelist2osv/converter.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323

2424
// ConversionMetrics holds the collected data about the conversion process for a single CVE.
2525
type ConversionMetrics struct {
26+
CVEID cves.CVEID `json:"id"` // The CVE ID
2627
CNA string `json:"cna"` // The CNA that assigned the CVE.
2728
Outcome string `json:"outcome"` // The final outcome of the conversion (e.g., "Successful", "Failed").
2829
Repos []string `json:"repos"` // A list of repositories extracted from the CVE's references.
@@ -31,6 +32,17 @@ type ConversionMetrics struct {
3132
Notes []string `json:"notes"` // A collection of notes and warnings generated during conversion.
3233
}
3334

35+
// AddNote adds a formatted note to the ConversionMetrics.
36+
func (m *ConversionMetrics) AddNote(format string, a ...any) {
37+
m.Notes = append(m.Notes, fmt.Sprintf(format, a...))
38+
logger.Debug(fmt.Sprintf(format, a...), slog.String("cna", m.CNA), slog.String("cve", string(m.CVEID)))
39+
}
40+
41+
// AddSource appends a source to the ConversionMetrics
42+
func (m *ConversionMetrics) AddSource(source VersionSource) {
43+
m.VersionSources = append(m.VersionSources, source)
44+
}
45+
3446
// RefTagDenyList contains reference tags that are often associated with unreliable or
3547
// irrelevant repository URLs. References with these tags are currently ignored
3648
// to avoid incorrect repository associations.
@@ -55,7 +67,7 @@ func extractConversionMetrics(cve cves.CVE5, refs []osvschema.Reference, metrics
5567
}
5668
metrics.RefTypesCount = refTypeCounts
5769
for refType, count := range refTypeCounts {
58-
metrics.Notes = append(metrics.Notes, fmt.Sprintf("[%s]: Reference Type %s: %d", cve.Metadata.CVEID, refType, count))
70+
metrics.AddNote("[%s]: Reference Type %s: %d", cve.Metadata.CVEID, refType, count)
5971
}
6072

6173
// TODO(jesslowe): Add more analysis based on ADP containers, CVSS, KEV, CWE, etc.
@@ -79,14 +91,14 @@ func FromCVE5(cve cves.CVE5, refs []cves.Reference, metrics *ConversionMetrics)
7991

8092
published, err := cves.ParseCVE5Timestamp(cve.Metadata.DatePublished)
8193
if err != nil {
82-
metrics.Notes = append(metrics.Notes, "Published date failed to parse, setting time to now")
94+
metrics.AddNote("[%s]: Published date failed to parse, setting time to now", cve.Metadata.CVEID)
8395
published = time.Now()
8496
}
8597
v.Published = published
8698

8799
modified, err := cves.ParseCVE5Timestamp(cve.Metadata.DateUpdated)
88100
if err != nil {
89-
metrics.Notes = append(metrics.Notes, "Modified date failed to parse, setting time to now")
101+
metrics.AddNote("[%s]: Modified date failed to parse, setting time to now", cve.Metadata.CVEID)
90102
modified = time.Now()
91103
}
92104
v.Modified = modified
@@ -166,7 +178,7 @@ func ConvertAndExportCVEToOSV(cve cves.CVE5, directory string) error {
166178
cveID := cve.Metadata.CVEID
167179
cnaAssigner := cve.Metadata.AssignerShortName
168180
references := identifyPossibleURLs(cve)
169-
metrics := &ConversionMetrics{}
181+
metrics := &ConversionMetrics{CVEID: cveID, CNA: cnaAssigner}
170182
// Create a base OSV record from the CVE.
171183
v := FromCVE5(cve, references, metrics)
172184

@@ -175,7 +187,9 @@ func ConvertAndExportCVEToOSV(cve cves.CVE5, directory string) error {
175187

176188
// Try to extract repository URLs from references.
177189
repos, repoNotes := cves.ReposFromReferencesCVEList(string(cveID), references, RefTagDenyList)
178-
metrics.Notes = append(metrics.Notes, repoNotes...)
190+
for _, note := range repoNotes {
191+
metrics.AddNote("%s", note)
192+
}
179193
metrics.Repos = repos
180194

181195
vulnDir := filepath.Join(directory, cnaAssigner)

vulnfeeds/cmd/cvelist2osv/version_extraction.go

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -125,19 +125,19 @@ func AddVersionInfo(cve cves.CVE5, v *vulns.Vulnerability, metrics *ConversionMe
125125

126126
v.Affected = append(v.Affected, aff)
127127
if hasGit {
128-
metrics.VersionSources = append(metrics.VersionSources, VersionSourceGit)
128+
metrics.AddSource(VersionSourceGit)
129129
} else {
130-
metrics.VersionSources = append(metrics.VersionSources, VersionSourceAffected)
130+
metrics.AddSource(VersionSourceAffected)
131131
}
132132
}
133133

134134
// If no versions were found so far, fall back to CPEs.
135135
if !gotVersions {
136-
metrics.Notes = append(metrics.Notes, "No versions in affected, attempting to extract from CPE")
136+
metrics.AddNote("No versions in affected, attempting to extract from CPE")
137137
cpeRanges, cpeStrings, err := findCPEVersionRanges(cve)
138138
if err == nil && len(cpeRanges) > 0 {
139139
gotVersions = true
140-
metrics.VersionSources = append(metrics.VersionSources, VersionSourceCPE)
140+
metrics.AddSource(VersionSourceCPE)
141141
aff := osvschema.Affected{}
142142
for _, vr := range cpeRanges {
143143
vr.Type = osvschema.RangeEcosystem
@@ -147,19 +147,21 @@ func AddVersionInfo(cve cves.CVE5, v *vulns.Vulnerability, metrics *ConversionMe
147147
aff.DatabaseSpecific["CPEs"] = vulns.Unique(cpeStrings)
148148
v.Affected = append(v.Affected, aff)
149149
} else if err != nil {
150-
metrics.Notes = append(metrics.Notes, err.Error())
150+
metrics.AddNote("%s", err.Error())
151151
}
152152
}
153153

154154
// As a last resort, try extracting versions from the description text.
155155
if !gotVersions {
156-
metrics.Notes = append(metrics.Notes, "No versions in CPEs so attempting extraction from description")
156+
metrics.AddNote("No versions in CPEs so attempting extraction from description")
157157
versions, extractNotes := cves.ExtractVersionsFromText(nil, cves.EnglishDescription(cve.Containers.CNA.Descriptions))
158-
metrics.Notes = append(metrics.Notes, extractNotes...)
158+
for _, note := range extractNotes {
159+
metrics.AddNote("%s", note)
160+
}
159161
if len(versions) > 0 {
160162
// NOTE: These versions are not currently saved due to the need for better validation.
161-
metrics.VersionSources = append(metrics.VersionSources, VersionSourceDescription)
162-
metrics.Notes = append(metrics.Notes, fmt.Sprintf("Extracted versions from description but did not save them: %+v", versions))
163+
metrics.AddSource(VersionSourceDescription)
164+
metrics.AddNote("Extracted versions from description but did not save them: %+v", versions)
163165
}
164166
}
165167
}
@@ -225,7 +227,7 @@ func extractVersionsFromAffectedField(affected cves.Affected, cnaAssigner string
225227
// It sorts the introduced and fixed versions to create chronological ranges.
226228
func findInverseAffectedRanges(cveAff cves.Affected, cnaAssigner string, metrics *ConversionMetrics) (ranges []osvschema.Range, versType VersionRangeType) {
227229
if cnaAssigner != "Linux" {
228-
metrics.Notes = append(metrics.Notes, "Currently only supporting Linux inverse logic")
230+
metrics.AddNote("Currently only supporting Linux inverse logic")
229231
return nil, VersionRangeTypeUnknown
230232
}
231233
var introduced []string
@@ -240,7 +242,7 @@ func findInverseAffectedRanges(cveAff cves.Affected, cnaAssigner string, metrics
240242
case 3:
241243
introduced = append(introduced, versionValue)
242244
default:
243-
metrics.Notes = append(metrics.Notes, "Bad non-semver version given: "+versionValue)
245+
metrics.AddNote("Bad non-semver version given: %s", versionValue)
244246
continue
245247
}
246248
}
@@ -275,15 +277,15 @@ func findInverseAffectedRanges(cveAff cves.Affected, cnaAssigner string, metrics
275277
for index, f := range fixed {
276278
if index < len(introduced) {
277279
ranges = append(ranges, buildVersionRange(introduced[index], "", f))
278-
metrics.Notes = append(metrics.Notes, "Introduced from version value - "+introduced[index])
279-
metrics.Notes = append(metrics.Notes, "Fixed from version value - "+f)
280+
metrics.AddNote("Introduced from version value - %s", introduced[index])
281+
metrics.AddNote("Fixed from version value - %s", f)
280282
}
281283
}
282284

283285
if len(ranges) != 0 {
284286
return ranges, VersionRangeTypeSemver
285287
}
286-
metrics.Notes = append(metrics.Notes, "no ranges found")
288+
metrics.AddNote("no ranges found")
287289

288290
return nil, VersionRangeTypeUnknown
289291
}
@@ -304,30 +306,30 @@ func findNormalAffectedRanges(affected cves.Affected, metrics *ConversionMetrics
304306
// Quality check the version strings to avoid using filler content.
305307
vQuality := vulns.CheckQuality(vers.Version)
306308
if !vQuality.AtLeast(acceptableQuality) {
307-
metrics.Notes = append(metrics.Notes, fmt.Sprintf("Version value for %s %s is filler or empty", affected.Vendor, affected.Product))
309+
metrics.AddNote("Version value for %s %s is filler or empty", affected.Vendor, affected.Product)
308310
}
309311
vLessThanQual := vulns.CheckQuality(vers.LessThan)
310312
vLTOEQual := vulns.CheckQuality(vers.LessThanOrEqual)
311313

312314
hasRange := vLessThanQual.AtLeast(acceptableQuality) || vLTOEQual.AtLeast(acceptableQuality)
313-
metrics.Notes = append(metrics.Notes, fmt.Sprintf("Range detected: %v", hasRange))
315+
metrics.AddNote("Range detected: %v", hasRange)
314316
// Handle cases where 'lessThan' is mistakenly the same as 'version'.
315317
if vers.LessThan != "" && vers.LessThan == vers.Version {
316-
metrics.Notes = append(metrics.Notes, fmt.Sprintf("Warning: lessThan (%s) is the same as introduced (%s)\n", vers.LessThan, vers.Version))
318+
metrics.AddNote("Warning: lessThan (%s) is the same as introduced (%s)\n", vers.LessThan, vers.Version)
317319
hasRange = false
318320
}
319321

320322
if hasRange {
321323
if vQuality.AtLeast(acceptableQuality) {
322324
introduced = vers.Version
323-
metrics.Notes = append(metrics.Notes, fmt.Sprintf("%s - Introduced from version value - %s", vQuality.String(), vers.Version))
325+
metrics.AddNote("%s - Introduced from version value - %s", vQuality.String(), vers.Version)
324326
}
325327
if vLessThanQual.AtLeast(acceptableQuality) {
326328
fixed = vers.LessThan
327-
metrics.Notes = append(metrics.Notes, fmt.Sprintf("%s - Fixed from LessThan value - %s", vLessThanQual.String(), vers.LessThan))
329+
metrics.AddNote("%s - Fixed from LessThan value - %s", vLessThanQual.String(), vers.LessThan)
328330
} else if vLTOEQual.AtLeast(acceptableQuality) {
329331
lastaffected = vers.LessThanOrEqual
330-
metrics.Notes = append(metrics.Notes, fmt.Sprintf("%s - LastAffected from LessThanOrEqual value- %s", vLTOEQual.String(), vers.LessThanOrEqual))
332+
metrics.AddNote("%s - LastAffected from LessThanOrEqual value- %s", vLTOEQual.String(), vers.LessThanOrEqual)
331333
}
332334

333335
if introduced != "" && fixed != "" {
@@ -342,7 +344,7 @@ func findNormalAffectedRanges(affected cves.Affected, metrics *ConversionMetrics
342344
// In this case only vers.Version exists which either means that it is _only_ that version that is
343345
// affected, but more likely, it affects up to that version. It could also mean that the range is given
344346
// in one line instead - like "< 1.5.3" or "< 2.45.4, >= 2.0 " or just "before 1.4.7", so check for that.
345-
metrics.Notes = append(metrics.Notes, "Only version exists")
347+
metrics.AddNote("Only version exists")
346348
// GitHub often encodes the range directly in the version string.
347349

348350
av, err := git.ParseVersionRange(vers.Version)
@@ -365,12 +367,14 @@ func findNormalAffectedRanges(affected cves.Affected, metrics *ConversionMetrics
365367
}
366368

367369
// Try to extract versions from text like "before 1.4.7".
368-
possibleVersions, note := cves.ExtractVersionsFromText(nil, vers.Version)
369-
if note != nil {
370-
metrics.Notes = append(metrics.Notes, note...)
370+
possibleVersions, notes := cves.ExtractVersionsFromText(nil, vers.Version)
371+
372+
for _, note := range notes {
373+
metrics.AddNote("%s", note)
371374
}
375+
372376
if possibleVersions != nil {
373-
metrics.Notes = append(metrics.Notes, "Versions retrieved from text but not used CURRENTLY")
377+
metrics.AddNote("Versions retrieved from text but not used CURRENTLY")
374378
continue
375379
}
376380

0 commit comments

Comments
 (0)