diff --git a/cve_bin_tool/cve_scanner.py b/cve_bin_tool/cve_scanner.py index 210772cd7c..1277380bd6 100644 --- a/cve_bin_tool/cve_scanner.py +++ b/cve_bin_tool/cve_scanner.py @@ -11,7 +11,7 @@ from typing import DefaultDict, Dict, List, Tuple from packaging.version import Version -from packaging.version import parse as parse_version +from packaging.version import parse as packaging_parse from rich.console import Console from cve_bin_tool.cvedb import DBNAME, DISK_LOCATION_DEFAULT @@ -97,7 +97,7 @@ def get_cves(self, product_info: ProductInfo, triage_data: TriageData): # Need to manipulate version to ensure canonical form of version - parsed_version, parsed_version_between = self.canonical_convert(product_info) + parsed_version = self.canonical_convert(product_info) # If canonical form of version numbering not found, exit if parsed_version == "UNKNOWN": return @@ -139,19 +139,18 @@ def get_cves(self, product_info: ProductInfo, triage_data: TriageData): version_start_excluding = self.letter_convert(version_start_excluding) version_end_including = self.letter_convert(version_end_including) version_end_excluding = self.letter_convert(version_end_excluding) - parsed_version = parsed_version_between # check the start range passes_start = False if ( version_start_including is not self.RANGE_UNSET - and parsed_version >= parse_version(version_start_including) + and parsed_version >= self.parse_version(version_start_including) ): passes_start = True if ( version_start_excluding is not self.RANGE_UNSET - and parsed_version > parse_version(version_start_excluding) + and parsed_version > self.parse_version(version_start_excluding) ): passes_start = True @@ -166,13 +165,13 @@ def get_cves(self, product_info: ProductInfo, triage_data: TriageData): passes_end = False if ( version_end_including is not self.RANGE_UNSET - and parsed_version <= parse_version(version_end_including) + and parsed_version <= self.parse_version(version_end_including) ): passes_end = True if ( version_end_excluding is not self.RANGE_UNSET - and parsed_version < parse_version(version_end_excluding) + and parsed_version < self.parse_version(version_end_excluding) ): passes_end = True @@ -307,17 +306,15 @@ def letter_convert(self, version: str) -> str: version = f"{version[:-1]}.{self.ALPHA_TO_NUM[last_char]}" return version - VersionType = Version - - def canonical_convert( - self, product_info: ProductInfo - ) -> Tuple[VersionType, VersionType]: - version_between = parse_version("") + def canonical_convert(self, product_info: ProductInfo) -> Tuple[Version]: if product_info.version == "": - return parse_version(product_info.version), version_between + return self.parse_version(product_info.version) + + # if we're using openssl or libjpeg, we need to convert letters to numbers if product_info.product in {"openssl", "libjpeg"}: pv = re.search(r"\d[.\d]*[a-z]?", product_info.version) - version_between = parse_version(self.letter_convert(pv.group(0))) + parsed_version = self.parse_version(self.letter_convert(pv.group(0))) + else: # Ensure canonical form of version numbering if ":" in product_info.version: @@ -333,8 +330,26 @@ def canonical_convert( f"error parsing {product_info.vendor}.{product_info.product} v{product_info.version} - manual inspection required" ) else: - parsed_version = parse_version(pv.group(0)) - return parsed_version, version_between + parsed_version = self.parse_version(pv.group(0)) + return parsed_version + + def parse_version(self, version_string: str) -> Tuple[Version]: + # convert _ to . for versions 1_2_3 -> 1.2.3 + version_string = re.sub("_", ".", version_string) + + # handle leading product name like curl-7.57.0 by truncating + version_string = re.sub("[a-zA-Z]*-", "", version_string) + + # Or just convert the - to a . for cases like `5-1.6` + version_string = re.sub("-", ".", version_string) + + # handle trailing letters a la '1.6.3.kdc' by truncating + version_string = re.sub(r"\.[a-zA-Z]*", "", version_string) + + # Otherwise just try to parse it with packaging's parse + parsed_version = packaging_parse(version_string) + + return parsed_version def affected(self): """Returns list of vendor.product and version tuples identified from diff --git a/requirements.txt b/requirements.txt index 45c67dc97e..3e22f2349d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ jinja2>=2.11.3 jsonschema>=3.0.2 lib4sbom>=0.3.0 python-gnupg -packaging<22.0 +packaging plotly pyyaml>=5.4 requests