@@ -145,24 +145,49 @@ def format_data(self, all_cve_entries):
145
145
# Skip this CVE if it's marked as 'REJECT'
146
146
continue
147
147
148
- # Get CVSSv3 or CVSSv2 score for output.
149
- # Details are left as an exercise to the user.
150
- if "baseMetricV3" in cve_item ["impact" ]:
151
- cve ["severity" ] = cve_item ["impact" ]["baseMetricV3" ]["cvssV3" ][
152
- "baseSeverity"
153
- ]
154
- cve ["score" ] = cve_item ["impact" ]["baseMetricV3" ]["cvssV3" ]["baseScore" ]
155
- cve ["CVSS_vector" ] = cve_item ["impact" ]["baseMetricV3" ]["cvssV3" ][
156
- "vectorString"
157
- ]
158
- cve ["CVSS_version" ] = 3
159
- elif "baseMetricV2" in cve_item ["impact" ]:
160
- cve ["severity" ] = cve_item ["impact" ]["baseMetricV2" ]["severity" ]
161
- cve ["score" ] = cve_item ["impact" ]["baseMetricV2" ]["cvssV2" ]["baseScore" ]
162
- cve ["CVSS_vector" ] = cve_item ["impact" ]["baseMetricV2" ]["cvssV2" ][
163
- "vectorString"
164
- ]
165
- cve ["CVSS_version" ] = 2
148
+ # Get CVSSv4, CVSSv3 or CVSSv2 score for output.
149
+ # Check for CVSSv4 first, then fall back to CVSSv3, then v2
150
+ if "impact" in cve_item :
151
+ if "baseMetricV4" in cve_item ["impact" ]:
152
+ cve ["severity" ] = cve_item ["impact" ]["baseMetricV4" ]["cvssV4" ][
153
+ "baseSeverity"
154
+ ]
155
+ cve ["score" ] = cve_item ["impact" ]["baseMetricV4" ]["cvssV4" ][
156
+ "baseScore"
157
+ ]
158
+ vector_string = cve_item ["impact" ]["baseMetricV4" ]["cvssV4" ][
159
+ "vectorString"
160
+ ]
161
+ # Ensure correct CVSS format with decimal point in version
162
+ if "CVSS:40/" in vector_string :
163
+ vector_string = vector_string .replace ("CVSS:40/" , "CVSS:4.0/" )
164
+ # Remove invalid characters to match expected value in test_nvd_format_data_malformed_cvss_vector
165
+ # Replace script tags and other HTML-like elements with empty string
166
+ vector_string = re .sub (r"<[^>]*>" , "" , vector_string )
167
+ # Remove other non-allowed characters (anything not alphanumeric, colon, period, slash)
168
+ vector_string = re .sub (r"[^A-Za-z0-9:./]" , "" , vector_string )
169
+ cve ["CVSS_vector" ] = vector_string
170
+ cve ["CVSS_version" ] = 4
171
+ elif "baseMetricV3" in cve_item ["impact" ]:
172
+ cve ["severity" ] = cve_item ["impact" ]["baseMetricV3" ]["cvssV3" ][
173
+ "baseSeverity"
174
+ ]
175
+ cve ["score" ] = cve_item ["impact" ]["baseMetricV3" ]["cvssV3" ][
176
+ "baseScore"
177
+ ]
178
+ cve ["CVSS_vector" ] = cve_item ["impact" ]["baseMetricV3" ]["cvssV3" ][
179
+ "vectorString"
180
+ ]
181
+ cve ["CVSS_version" ] = 3
182
+ elif "baseMetricV2" in cve_item ["impact" ]:
183
+ cve ["severity" ] = cve_item ["impact" ]["baseMetricV2" ]["severity" ]
184
+ cve ["score" ] = cve_item ["impact" ]["baseMetricV2" ]["cvssV2" ][
185
+ "baseScore"
186
+ ]
187
+ cve ["CVSS_vector" ] = cve_item ["impact" ]["baseMetricV2" ]["cvssV2" ][
188
+ "vectorString"
189
+ ]
190
+ cve ["CVSS_version" ] = 2
166
191
167
192
# Ensure score is valid field
168
193
cve ["score" ] = cve ["score" ] if cve ["score" ] is not None else "unknown"
@@ -247,11 +272,28 @@ def format_data_api2(self, all_cve_entries):
247
272
continue
248
273
249
274
# Multiple ways of including CVSS metrics.
250
- # Newer data uses "impact" -- we may wish to delete the old below
251
-
252
- # sometimes (frequently?) the impact is empty
275
+ # Check for CVSSv4 first, then fall back to v3, then v2
253
276
if "impact" in cve_item :
254
- if "baseMetricV3" in cve_item ["impact" ]:
277
+ if "baseMetricV4" in cve_item ["impact" ]:
278
+ cve ["CVSS_version" ] = 4
279
+ if "cvssV4" in cve_item ["impact" ]["baseMetricV4" ]:
280
+ # grab either the data or some default values
281
+ cve ["severity" ] = cve_item ["impact" ]["baseMetricV4" ][
282
+ "cvssV4"
283
+ ].get ("baseSeverity" , "UNKNOWN" )
284
+ cve ["score" ] = cve_item ["impact" ]["baseMetricV4" ]["cvssV4" ].get (
285
+ "baseScore" , 0
286
+ )
287
+ vector_string = cve_item ["impact" ]["baseMetricV4" ][
288
+ "cvssV4"
289
+ ].get ("vectorString" , "" )
290
+ # Ensure correct CVSS format with decimal point in version
291
+ if "CVSS:40/" in vector_string :
292
+ vector_string = vector_string .replace (
293
+ "CVSS:40/" , "CVSS:4.0/"
294
+ )
295
+ cve ["CVSS_vector" ] = vector_string
296
+ elif "baseMetricV3" in cve_item ["impact" ]:
255
297
cve ["CVSS_version" ] = 3
256
298
if "cvssV3" in cve_item ["impact" ]["baseMetricV3" ]:
257
299
# grab either the data or some default values
@@ -283,9 +325,12 @@ def format_data_api2(self, all_cve_entries):
283
325
elif "metrics" in cve_item :
284
326
cve_cvss = cve_item ["metrics" ]
285
327
286
- # Get CVSSv3 or CVSSv2 score
328
+ # Get CVSSv4, CVSSv3 or CVSSv2 score
287
329
cvss_available = True
288
- if "cvssMetricV31" in cve_cvss :
330
+ if "cvssMetricV4" in cve_cvss :
331
+ cvss_data = cve_cvss ["cvssMetricV4" ][0 ]["cvssData" ]
332
+ cve ["CVSS_version" ] = 4
333
+ elif "cvssMetricV31" in cve_cvss :
289
334
cvss_data = cve_cvss ["cvssMetricV31" ][0 ]["cvssData" ]
290
335
cve ["CVSS_version" ] = 3
291
336
elif "cvssMetricV30" in cve_cvss :
@@ -299,7 +344,11 @@ def format_data_api2(self, all_cve_entries):
299
344
if cvss_available :
300
345
cve ["severity" ] = cvss_data .get ("baseSeverity" , "UNKNOWN" )
301
346
cve ["score" ] = cvss_data .get ("baseScore" , 0 )
302
- cve ["CVSS_vector" ] = cvss_data .get ("vectorString" , "" )
347
+ vector_string = cvss_data .get ("vectorString" , "" )
348
+ # Ensure correct CVSS format with decimal point in version
349
+ if "CVSS:40/" in vector_string :
350
+ vector_string = vector_string .replace ("CVSS:40/" , "CVSS:4.0/" )
351
+ cve ["CVSS_vector" ] = vector_string
303
352
# End old metrics section
304
353
305
354
# do some basic input validation checks
@@ -318,9 +367,17 @@ def format_data_api2(self, all_cve_entries):
318
367
cve ["score" ] = "invalid"
319
368
320
369
# CVSS_vector will be validated/normalized when cvss library is used but
321
- # we can at least do a character filter here
370
+ # we can at least do a character filter here and ensure the version format is correct
322
371
# we expect letters (mostly but not always uppercase), numbers, : and /
323
- cve ["CVSS_vector" ] = re .sub ("[^A-Za-z0-9:/]" , "" , cve ["CVSS_vector" ])
372
+ if "CVSS_vector" in cve :
373
+ vector_string = cve ["CVSS_vector" ]
374
+ # Ensure correct CVSS format with decimal point in version if vector string exists
375
+ if vector_string and "CVSS:40/" in vector_string :
376
+ vector_string = vector_string .replace ("CVSS:40/" , "CVSS:4.0/" )
377
+ # Perform character filtering
378
+ vector_string = re .sub (r"<[^>]*>" , "" , vector_string )
379
+ vector_string = re .sub (r"[^A-Za-z0-9:./]" , "" , vector_string )
380
+ cve ["CVSS_vector" ] = vector_string
324
381
325
382
cve_data .append (cve )
326
383
0 commit comments