@@ -72,9 +72,7 @@ func toVersionRangeType(s string) VersionRangeType {
72
72
// 3. If no versions are found, it falls back to searching for CPEs in the CNA container.
73
73
// 4. As a last resort, it attempts to extract version information from the description text (currently not saved).
74
74
// It returns the source of the version information and a slice of notes detailing the extraction process.
75
- func AddVersionInfo (cve cves.CVE5 , v * vulns.Vulnerability ) ([]VersionSource , []string ) {
76
- var notes []string
77
- var source []VersionSource
75
+ func AddVersionInfo (cve cves.CVE5 , v * vulns.Vulnerability , metrics * ConversionMetrics ) {
78
76
gotVersions := false
79
77
80
78
// Combine 'affected' entries from both CNA and ADP containers.
@@ -91,12 +89,11 @@ func AddVersionInfo(cve cves.CVE5, v *vulns.Vulnerability) ([]VersionSource, []s
91
89
// Attempt to extract version ranges from the combined 'affected' fields.
92
90
hasGit := false
93
91
for _ , cveAff := range affected {
94
- versionRanges , versionType , extractNotes := extractVersionsFromAffectedField (cveAff , cve .Metadata .AssignerShortName )
92
+ versionRanges , versionType := extractVersionsFromAffectedField (cveAff , cve .Metadata .AssignerShortName , metrics )
95
93
// TODO(jesslowe): update this to be more elegant (currently skips retrieving more git ranges after the first)
96
94
if versionType == VersionRangeTypeGit && hasGit {
97
95
continue
98
96
}
99
- notes = append (notes , extractNotes ... )
100
97
101
98
if len (versionRanges ) == 0 {
102
99
continue
@@ -128,19 +125,19 @@ func AddVersionInfo(cve cves.CVE5, v *vulns.Vulnerability) ([]VersionSource, []s
128
125
129
126
v .Affected = append (v .Affected , aff )
130
127
if hasGit {
131
- source = append (source , VersionSourceGit )
128
+ metrics . VersionSources = append (metrics . VersionSources , VersionSourceGit )
132
129
} else {
133
- source = append (source , VersionSourceAffected )
130
+ metrics . VersionSources = append (metrics . VersionSources , VersionSourceAffected )
134
131
}
135
132
}
136
133
137
134
// If no versions were found so far, fall back to CPEs.
138
135
if ! gotVersions {
139
- notes = append (notes , "No versions in affected, attempting to extract from CPE" )
136
+ metrics . Notes = append (metrics . Notes , "No versions in affected, attempting to extract from CPE" )
140
137
cpeRanges , cpeStrings , err := findCPEVersionRanges (cve )
141
138
if err == nil && len (cpeRanges ) > 0 {
142
139
gotVersions = true
143
- source = append (source , VersionSourceCPE )
140
+ metrics . VersionSources = append (metrics . VersionSources , VersionSourceCPE )
144
141
aff := osvschema.Affected {}
145
142
for _ , vr := range cpeRanges {
146
143
vr .Type = osvschema .RangeEcosystem
@@ -150,23 +147,21 @@ func AddVersionInfo(cve cves.CVE5, v *vulns.Vulnerability) ([]VersionSource, []s
150
147
aff .DatabaseSpecific ["CPEs" ] = vulns .Unique (cpeStrings )
151
148
v .Affected = append (v .Affected , aff )
152
149
} else if err != nil {
153
- notes = append (notes , err .Error ())
150
+ metrics . Notes = append (metrics . Notes , err .Error ())
154
151
}
155
152
}
156
153
157
154
// As a last resort, try extracting versions from the description text.
158
155
if ! gotVersions {
159
- notes = append (notes , "No versions in CPEs so attempting extraction from description" )
156
+ metrics . Notes = append (metrics . Notes , "No versions in CPEs so attempting extraction from description" )
160
157
versions , extractNotes := cves .ExtractVersionsFromText (nil , cves .EnglishDescription (cve .Containers .CNA .Descriptions ))
161
- notes = append (notes , extractNotes ... )
158
+ metrics . Notes = append (metrics . Notes , extractNotes ... )
162
159
if len (versions ) > 0 {
163
160
// NOTE: These versions are not currently saved due to the need for better validation.
164
- source = append (source , VersionSourceDescription )
165
- notes = append (notes , fmt .Sprintf ("Extracted versions from description but did not save them: %+v" , versions ))
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 ))
166
163
}
167
164
}
168
-
169
- return source , notes
170
165
}
171
166
172
167
// findCPEVersionRanges extracts version ranges and CPE strings from the CNA's
@@ -214,23 +209,24 @@ func findCPEVersionRanges(cve cves.CVE5) (versionRanges []osvschema.Range, cpes
214
209
// - As a fallback, it may assume a single version means "fixed at this version, introduced at 0".
215
210
//
216
211
// Returns the extracted OSV ranges, the most frequent version type (e.g., "semver"), and any notes.
217
- func extractVersionsFromAffectedField (affected cves.Affected , cnaAssigner string ) ([]osvschema.Range , VersionRangeType , [] string ) {
212
+ func extractVersionsFromAffectedField (affected cves.Affected , cnaAssigner string , metrics * ConversionMetrics ) ([]osvschema.Range , VersionRangeType ) {
218
213
// Handle cases where a product is marked as "affected" by default, and specific versions are marked "unaffected".
219
214
if affected .DefaultStatus == "affected" {
220
215
// Calculate the affected ranges by finding the inverse of the unaffected ranges.
221
- return findInverseAffectedRanges (affected , cnaAssigner )
216
+ return findInverseAffectedRanges (affected , cnaAssigner , metrics )
222
217
}
223
218
224
- return findNormalAffectedRanges (affected , cnaAssigner )
219
+ return findNormalAffectedRanges (affected , cnaAssigner , metrics )
225
220
}
226
221
227
222
// findInverseAffectedRanges calculates the affected version ranges by analyzing a list
228
223
// of 'unaffected' versions. This is common in Linux kernel CVEs where a product is
229
224
// considered affected by default, and only unaffected versions are listed.
230
225
// It sorts the introduced and fixed versions to create chronological ranges.
231
- func findInverseAffectedRanges (cveAff cves.Affected , cnaAssigner string ) (ranges []osvschema.Range , versType VersionRangeType , notes [] string ) {
226
+ func findInverseAffectedRanges (cveAff cves.Affected , cnaAssigner string , metrics * ConversionMetrics ) (ranges []osvschema.Range , versType VersionRangeType ) {
232
227
if cnaAssigner != "Linux" {
233
- return nil , VersionRangeTypeUnknown , append (notes , "Currently only supporting Linux inverse logic" )
228
+ metrics .Notes = append (metrics .Notes , "Currently only supporting Linux inverse logic" )
229
+ return nil , VersionRangeTypeUnknown
234
230
}
235
231
var introduced []string
236
232
fixed := make ([]string , 0 , len (cveAff .Versions ))
@@ -244,7 +240,7 @@ func findInverseAffectedRanges(cveAff cves.Affected, cnaAssigner string) (ranges
244
240
case 3 :
245
241
introduced = append (introduced , versionValue )
246
242
default :
247
- notes = append (notes , "Bad non-semver version given: " + versionValue )
243
+ metrics . Notes = append (metrics . Notes , "Bad non-semver version given: " + versionValue )
248
244
continue
249
245
}
250
246
}
@@ -279,20 +275,20 @@ func findInverseAffectedRanges(cveAff cves.Affected, cnaAssigner string) (ranges
279
275
for index , f := range fixed {
280
276
if index < len (introduced ) {
281
277
ranges = append (ranges , buildVersionRange (introduced [index ], "" , f ))
282
- notes = append (notes , "Introduced from version value - " + introduced [index ])
283
- notes = append (notes , "Fixed from version value - " + f )
278
+ metrics . Notes = append (metrics . Notes , "Introduced from version value - " + introduced [index ])
279
+ metrics . Notes = append (metrics . Notes , "Fixed from version value - " + f )
284
280
}
285
281
}
286
282
287
283
if len (ranges ) != 0 {
288
- return ranges , VersionRangeTypeSemver , notes
284
+ return ranges , VersionRangeTypeSemver
289
285
}
290
- notes = append (notes , "no ranges found" )
286
+ metrics . Notes = append (metrics . Notes , "no ranges found" )
291
287
292
- return nil , VersionRangeTypeUnknown , notes
288
+ return nil , VersionRangeTypeUnknown
293
289
}
294
290
295
- func findNormalAffectedRanges (affected cves.Affected , cnaAssigner string ) (versionRanges []osvschema.Range , versType VersionRangeType , notes [] string ) {
291
+ func findNormalAffectedRanges (affected cves.Affected , cnaAssigner string , metrics * ConversionMetrics ) (versionRanges []osvschema.Range , versType VersionRangeType ) {
296
292
versionTypesCount := make (map [VersionRangeType ]int )
297
293
298
294
for _ , vers := range affected .Versions {
@@ -308,30 +304,30 @@ func findNormalAffectedRanges(affected cves.Affected, cnaAssigner string) (versi
308
304
// Quality check the version strings to avoid using filler content.
309
305
vQuality := vulns .CheckQuality (vers .Version )
310
306
if ! vQuality .AtLeast (acceptableQuality ) {
311
- notes = append (notes , fmt .Sprintf ("Version value for %s %s is filler or empty" , affected .Vendor , affected .Product ))
307
+ metrics . Notes = append (metrics . Notes , fmt .Sprintf ("Version value for %s %s is filler or empty" , affected .Vendor , affected .Product ))
312
308
}
313
309
vLessThanQual := vulns .CheckQuality (vers .LessThan )
314
310
vLTOEQual := vulns .CheckQuality (vers .LessThanOrEqual )
315
311
316
312
hasRange := vLessThanQual .AtLeast (acceptableQuality ) || vLTOEQual .AtLeast (acceptableQuality )
317
- notes = append (notes , fmt .Sprintf ("Range detected: %v" , hasRange ))
313
+ metrics . Notes = append (metrics . Notes , fmt .Sprintf ("Range detected: %v" , hasRange ))
318
314
// Handle cases where 'lessThan' is mistakenly the same as 'version'.
319
315
if vers .LessThan != "" && vers .LessThan == vers .Version {
320
- notes = append (notes , fmt .Sprintf ("Warning: lessThan (%s) is the same as introduced (%s)\n " , 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 ))
321
317
hasRange = false
322
318
}
323
319
324
320
if hasRange {
325
321
if vQuality .AtLeast (acceptableQuality ) {
326
322
introduced = vers .Version
327
- notes = append (notes , fmt .Sprintf ("%s - Introduced from version value - %s" , vQuality .String (), vers .Version ))
323
+ metrics . Notes = append (metrics . Notes , fmt .Sprintf ("%s - Introduced from version value - %s" , vQuality .String (), vers .Version ))
328
324
}
329
325
if vLessThanQual .AtLeast (acceptableQuality ) {
330
326
fixed = vers .LessThan
331
- notes = append (notes , fmt .Sprintf ("%s - Fixed from LessThan value - %s" , vLessThanQual .String (), vers .LessThan ))
327
+ metrics . Notes = append (metrics . Notes , fmt .Sprintf ("%s - Fixed from LessThan value - %s" , vLessThanQual .String (), vers .LessThan ))
332
328
} else if vLTOEQual .AtLeast (acceptableQuality ) {
333
329
lastaffected = vers .LessThanOrEqual
334
- notes = append (notes , fmt .Sprintf ("%s - LastAffected from LessThanOrEqual value- %s" , vLTOEQual .String (), vers .LessThanOrEqual ))
330
+ metrics . Notes = append (metrics . Notes , fmt .Sprintf ("%s - LastAffected from LessThanOrEqual value- %s" , vLTOEQual .String (), vers .LessThanOrEqual ))
335
331
}
336
332
337
333
if introduced != "" && fixed != "" {
@@ -346,7 +342,7 @@ func findNormalAffectedRanges(affected cves.Affected, cnaAssigner string) (versi
346
342
// In this case only vers.Version exists which either means that it is _only_ that version that is
347
343
// affected, but more likely, it affects up to that version. It could also mean that the range is given
348
344
// in one line instead - like "< 1.5.3" or "< 2.45.4, >= 2.0 " or just "before 1.4.7", so check for that.
349
- notes = append (notes , "Only version exists" )
345
+ metrics . Notes = append (metrics . Notes , "Only version exists" )
350
346
// GitHub often encodes the range directly in the version string.
351
347
if cnaAssigner == "GitHub_M" {
352
348
av , err := git .ParseVersionRange (vers .Version )
@@ -372,17 +368,17 @@ func findNormalAffectedRanges(affected cves.Affected, cnaAssigner string) (versi
372
368
// Try to extract versions from text like "before 1.4.7".
373
369
possibleVersions , note := cves .ExtractVersionsFromText (nil , vers .Version )
374
370
if note != nil {
375
- notes = append (notes , note ... )
371
+ metrics . Notes = append (metrics . Notes , note ... )
376
372
}
377
373
if possibleVersions != nil {
378
- notes = append (notes , "Versions retrieved from text but not used CURRENTLY" )
374
+ metrics . Notes = append (metrics . Notes , "Versions retrieved from text but not used CURRENTLY" )
379
375
continue
380
376
}
381
377
382
378
// As a fallback, assume a single version means it's the fixed version.
383
379
if vQuality .AtLeast (acceptableQuality ) {
384
380
versionRanges = append (versionRanges , buildVersionRange ("0" , "" , vers .Version ))
385
- notes = append (notes , fmt .Sprintf ("%s - Single version found %v - Assuming introduced = 0 and Fixed = %v" , vQuality , vers .Version , vers .Version ))
381
+ metrics . Notes = append (metrics . Notes , fmt .Sprintf ("%s - Single version found %v - Assuming introduced = 0 and Fixed = %v" , vQuality , vers .Version , vers .Version ))
386
382
}
387
383
}
388
384
@@ -396,7 +392,7 @@ func findNormalAffectedRanges(affected cves.Affected, cnaAssigner string) (versi
396
392
}
397
393
}
398
394
399
- return versionRanges , mostFrequentVersionType , notes
395
+ return versionRanges , mostFrequentVersionType
400
396
}
401
397
402
398
// buildVersionRange is a helper function that adds 'introduced', 'fixed', or 'last_affected'
0 commit comments