diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index 7b0b588d25..809caac273 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -22,6 +22,11 @@ class SBOMManager: + """ + SBOMManager is a class that manages the Software Bill of Materials (SBOM) data. + It provides methods for scanning SBOM files, parsing them, and retrieving vendor information. + """ + SBOMtype = ["spdx", "cyclonedx", "swid"] sbom_data: defaultdict[ProductInfo, TriageData] @@ -44,6 +49,51 @@ def __init__( # Connect to the database self.cvedb = CVEDB(version_check=False) + def common_prefix_split(self, product, version) -> list[ProductInfo]: + """If the product have '-' in name try splitting it and try common prefixes. + currently not being used, proposed to be used in future""" + parsed_data: list[ProductInfo] = [] + found_common_prefix = False + common_prefix = ( + "perl-", + "golang-", + "rubygem-", + "python-", + "py3-", + "python3-", + "python2-", + "rust-", + "nodejs-", + ) + for prefix in common_prefix: + if product.startswith(prefix): + common_prefix_product = product[len(prefix) :] + common_prefix_vendor = self.get_vendor(common_prefix_product) + if len(common_prefix_vendor) > 1 or ( + len(common_prefix_vendor) == 1 + and common_prefix_vendor[0] != "UNKNOWN" + ): + found_common_prefix = True + for vendor in common_prefix_vendor: + parsed_data.append( + ProductInfo(vendor, common_prefix_product, version) + ) + break + if not found_common_prefix: + # if vendor not found after removing common prefix try splitting it + LOGGER.debug( + f"No Vendor found for {product}, trying splitted product. " + "Some results may be inaccurate due to vendor identification limitations." + ) + splitted_product = product.split("-") + for sp in splitted_product: + temp = self.get_vendor(sp) + if len(temp) > 1 or (len(temp) == 1 and temp[0] != "UNKNOWN"): + for vendor in temp: + # if vendor is not None: + parsed_data.append(ProductInfo(vendor, sp, version)) + return parsed_data + def scan_file(self) -> dict[ProductInfo, TriageData]: self.logger.debug( f"Processing SBOM {self.filename} of type {self.type.upper()}" diff --git a/test/test_sbom.py b/test/test_sbom.py index 4020ed0a41..b42d1adae1 100644 --- a/test/test_sbom.py +++ b/test/test_sbom.py @@ -37,6 +37,10 @@ class TestSBOM: "paths": {""}, }, } + SPLIT_DATA = [ + ProductInfo(vendor="openzeppelin", product="contracts", version="4.8.1"), + ProductInfo(vendor="downline_goldmine", product="builder", version="3.2.4"), + ] @pytest.mark.parametrize( "filepath", @@ -118,6 +122,21 @@ def test_valid_swid_file( for p in swid_parsed_data: assert p in scan_result + @pytest.mark.parametrize( + "product, version, productinfo, no_existent_file", + [ + ("openzeppelin-contracts", "4.8.1", SPLIT_DATA[0], "no_existent_file"), + ("rubygem-builder", "3.2.4", SPLIT_DATA[1], "no_existent_file"), + ], + ) + def test_common_prefix_split(self, product, version, productinfo, no_existent_file): + """Unit Test for common_prefix_split that try to split on hyphen if no vendors are + are found and the product has hyphen, here a no_existent_file is used + with sole purpose for creating a SBOMManager instance""" + sbom_engine = SBOMManager(no_existent_file) + scanned_list = sbom_engine.common_prefix_split(product, version) + assert productinfo in scanned_list + @pytest.mark.parametrize( "filename, sbom_type, validate", (