From 95714d8c58e58ed7f4702407dceeca460c25593f Mon Sep 17 00:00:00 2001 From: Terri Oda Date: Thu, 29 Aug 2024 14:46:15 -0700 Subject: [PATCH] fix: modernize cvss score loading (#4373) * fixes #4370 Turns out our cvss score loading code was out of date and thus wasn't loading scores correctly and was throwing off a lot of log messages. I've also added some basic input validation to the cvss data just in case. Signed-off-by: Terri Oda --- cve_bin_tool/data_sources/nvd_source.py | 93 +++++++++++++++++++------ 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/cve_bin_tool/data_sources/nvd_source.py b/cve_bin_tool/data_sources/nvd_source.py index 73e4930a5d..1ee0a4d34b 100644 --- a/cve_bin_tool/data_sources/nvd_source.py +++ b/cve_bin_tool/data_sources/nvd_source.py @@ -247,27 +247,80 @@ def format_data_api2(self, all_cve_entries): continue # Multiple ways of including CVSS metrics. - cve_cvss = cve_item - if "metrics" in cve_item: + # Newer data uses "impact" -- we may wish to delete the old below + + # sometimes (frequently?) the impact is empty + if "impact" in cve_item: + if "baseMetricV3" in cve_item["impact"]: + cve["CVSS_version"] = 3 + if "cvssV3" in cve_item["impact"]["baseMetricV3"]: + # grab either the data or some default values + cve["severity"] = cve_item["impact"]["baseMetricV3"][ + "cvssV3" + ].get("baseSeverity", "UNKNOWN") + cve["score"] = cve_item["impact"]["baseMetricV3"]["cvssV3"].get( + "baseScore", 0 + ) + cve["CVSS_vector"] = cve_item["impact"]["baseMetricV3"][ + "cvssV3" + ].get("vectorString", "") + + # severity is in a different spot in v2 versus v3 + elif "baseMetricV2" in cve_item["impact"]: + cve["CVSS_version"] = 2 + cve["severity"] = cve_item["impact"]["baseMetricV4"].get( + "severity", "UNKNOWN" + ) + if "cvssV2" in cve_item["impact"]["baseMetricV2"]: + cve["score"] = cve_item["impact"]["baseMetricV2"]["cvssV2"].get( + "baseScore", 0 + ) + cve["CVSS_vector"] = cve_item["impact"]["baseMetricV2"][ + "cvssV2" + ].get("vectorString", "") + + # Old data used "metrics" -- This section may need to be deleted + elif "metrics" in cve_item: cve_cvss = cve_item["metrics"] - # Get CVSSv3 or CVSSv2 score - cvss_available = True - if "cvssMetricV31" in cve_cvss: - cvss_data = cve_cvss["cvssMetricV31"][0]["cvssData"] - cve["CVSS_version"] = 3 - elif "cvssMetricV30" in cve_cvss: - cvss_data = cve_cvss["cvssMetricV30"][0]["cvssData"] - cve["CVSS_version"] = 3 - elif "cvssMetricV2" in cve_cvss: - cvss_data = cve_cvss["cvssMetricV2"][0]["cvssData"] - cve["CVSS_version"] = 2 - else: - LOGGER.debug(f"Unknown CVSS metrics field {cve_item['id']}") - cvss_available = False - if cvss_available: - cve["severity"] = cvss_data.get("baseSeverity", "UNKNOWN") - cve["score"] = cvss_data.get("baseScore", 0) - cve["CVSS_vector"] = cvss_data.get("vectorString", "") + + # Get CVSSv3 or CVSSv2 score + cvss_available = True + if "cvssMetricV31" in cve_cvss: + cvss_data = cve_cvss["cvssMetricV31"][0]["cvssData"] + cve["CVSS_version"] = 3 + elif "cvssMetricV30" in cve_cvss: + cvss_data = cve_cvss["cvssMetricV30"][0]["cvssData"] + cve["CVSS_version"] = 3 + elif "cvssMetricV2" in cve_cvss: + cvss_data = cve_cvss["cvssMetricV2"][0]["cvssData"] + cve["CVSS_version"] = 2 + else: + cvss_available = False + if cvss_available: + cve["severity"] = cvss_data.get("baseSeverity", "UNKNOWN") + cve["score"] = cvss_data.get("baseScore", 0) + cve["CVSS_vector"] = cvss_data.get("vectorString", "") + # End old metrics section + + # do some basic input validation checks + # severity should be alphanumeric + if not cve["severity"].isalnum(): + self.logger.debug( + f"Severity for {cve['id']} is invalid: {cve['severity']}" + ) + cve["severity"] = re.sub(r"[\W]", "", cve["severity"]) + + # score should be numeric + try: + cve["score"] = float(cve["score"]) + except ValueError: + self.logger.debug(f"Score for {cve['id']} is invalid: {cve['score']}") + cve["score"] = "invalid" + + # CVSS_vector will be validated/normalized when cvss library is used but + # we can at least do a character filter here + # we expect letters (mostly but not always uppercase), numbers, : and / + cve["CVSS_vector"] = re.sub("[^A-Za-z0-9:/]", "", cve["CVSS_vector"]) cve_data.append(cve)