From f44970f9870458188c782464e207282bf843d1b4 Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Sat, 30 Mar 2024 00:45:39 +0530 Subject: [PATCH 01/15] feat: Adding locations in CycloneDX reports --- cve_bin_tool/input_engine.py | 11 ++-- cve_bin_tool/merge.py | 5 +- cve_bin_tool/output_engine/__init__.py | 6 +++ cve_bin_tool/output_engine/util.py | 2 + cve_bin_tool/parsers/__init__.py | 6 ++- cve_bin_tool/parsers/java.py | 5 +- cve_bin_tool/parsers/python.py | 5 +- cve_bin_tool/sbom_manager/__init__.py | 37 +++++++++++++- cve_bin_tool/sbom_manager/cyclonedx_parser.py | 12 +++-- cve_bin_tool/sbom_manager/spdx_parser.py | 6 ++- cve_bin_tool/util.py | 2 + cve_bin_tool/version_scanner.py | 35 ++++++++++++- test/test_available_fix.py | 21 ++++++-- test/test_exploits.py | 20 ++++++-- test/test_html.py | 12 ++--- test/test_input_engine.py | 48 ++++++++++++------ test/test_language_scanner.py | 4 +- test/test_merge.py | 7 ++- test/test_output_engine.py | 50 +++++++++++++------ test/test_package_list_parser.py | 29 +++++++++-- test/test_sbom.py | 16 ++++-- 21 files changed, 271 insertions(+), 68 deletions(-) diff --git a/cve_bin_tool/input_engine.py b/cve_bin_tool/input_engine.py index d5148671d1..f31bacb59e 100644 --- a/cve_bin_tool/input_engine.py +++ b/cve_bin_tool/input_engine.py @@ -261,7 +261,7 @@ def decode_bom_ref(self, ref) -> ProductInfo: urn_cdx = re.compile( r"urn:cdx:(?P.*?)\/(?P.*?)#(?P.*)" ) - + location = "location/to/product" if urn_cbt_ext_ref.match(ref): urn_dict = urn_cbt_ext_ref.match(ref).groupdict() vendor = urn_dict["vendor"] @@ -290,7 +290,9 @@ def decode_bom_ref(self, ref) -> ProductInfo: product_info = None if product is not None and self.validate_product(product): - product_info = ProductInfo(vendor.strip(), product.strip(), version.strip()) + product_info = ProductInfo( + vendor.strip(), product.strip(), version.strip(), location + ) return product_info @@ -314,7 +316,10 @@ def parse_data(self, fields: Set[str], data: Iterable) -> None: for row in data: product_info = ProductInfo( - row["vendor"].strip(), row["product"].strip(), row["version"].strip() + row["vendor"].strip(), + row["product"].strip(), + row["version"].strip(), + row.get("location", "location/to/product").strip(), ) self.parsed_data[product_info][ row.get("cve_number", "").strip() or "default" diff --git a/cve_bin_tool/merge.py b/cve_bin_tool/merge.py index cfc499c493..576bdead33 100644 --- a/cve_bin_tool/merge.py +++ b/cve_bin_tool/merge.py @@ -208,7 +208,10 @@ def parse_data_from_json( for row in json_data: product_info = ProductInfo( - row["vendor"].strip(), row["product"].strip(), row["version"].strip() + row["vendor"].strip(), + row["product"].strip(), + row["version"].strip(), + row.get("location", "location/to/product").strip(), ) parsed_data[product_info][row.get("cve_number", "").strip() or "default"] = { "remarks": Remarks(str(row.get("remarks", "")).strip()), diff --git a/cve_bin_tool/output_engine/__init__.py b/cve_bin_tool/output_engine/__init__.py index e7fb007d95..287b1cc909 100644 --- a/cve_bin_tool/output_engine/__init__.py +++ b/cve_bin_tool/output_engine/__init__.py @@ -106,6 +106,7 @@ def output_csv( "vendor", "product", "version", + "location", "cve_number", "severity", "score", @@ -917,6 +918,7 @@ def generate_sbom( sbom_relationships = [] my_package = SBOMPackage() sbom_relationship = SBOMRelationship() + # Create root package my_package.initialise() root_package = f'CVEBINTOOL-{Path(sbom_root).name.replace(".", "-")}' @@ -929,6 +931,7 @@ def generate_sbom( my_package.set_licensedeclared(license) my_package.set_licenseconcluded(license) my_package.set_supplier("UNKNOWN", "NOASSERTION") + # Store package data sbom_packages[(my_package.get_name(), my_package.get_value("version"))] = ( my_package.get_package() @@ -936,6 +939,7 @@ def generate_sbom( sbom_relationship.initialise() sbom_relationship.set_relationship(parent, "DESCRIBES", root_package) sbom_relationships.append(sbom_relationship.get_relationship()) + # Add dependent products for product_data in all_product_data: my_package.initialise() @@ -945,6 +949,8 @@ def generate_sbom( my_package.set_supplier("Organization", product_data.vendor) my_package.set_licensedeclared(license) my_package.set_licenseconcluded(license) + location = product_data.location + my_package.set_evidence(location) # Set location directly sbom_packages[(my_package.get_name(), my_package.get_value("version"))] = ( my_package.get_package() ) diff --git a/cve_bin_tool/output_engine/util.py b/cve_bin_tool/output_engine/util.py index fcbe5cc41b..62a758e0e5 100644 --- a/cve_bin_tool/output_engine/util.py +++ b/cve_bin_tool/output_engine/util.py @@ -160,6 +160,7 @@ def format_output( "vendor": "haxx" "product": "curl", "version": "1.2.1", + "location": "location/to/product", "cve_number": "CVE-1234-1234", "severity": "LOW", "score": "1.2", @@ -191,6 +192,7 @@ def format_output( "vendor": product_info.vendor, "product": product_info.product, "version": product_info.version, + "location": product_info.location, "cve_number": cve.cve_number, "severity": cve.severity, "score": str(cve.score), diff --git a/cve_bin_tool/parsers/__init__.py b/cve_bin_tool/parsers/__init__.py index e9cba77aaf..6963e2f2cc 100644 --- a/cve_bin_tool/parsers/__init__.py +++ b/cve_bin_tool/parsers/__init__.py @@ -62,18 +62,20 @@ def find_vendor(self, product, version): vendor_package_pair = self.cve_db.get_vendor_product_pairs(product) vendorlist: list[ScanInfo] = [] file_path = self.filename + location = file_path if vendor_package_pair != []: # To handle multiple vendors, return all combinations of product/vendor mappings for v in vendor_package_pair: vendor = v["vendor"] + location = v.get("location", "location/to/product") self.logger.debug(f"{file_path} {product} {version} by {vendor}") vendorlist.append( - ScanInfo(ProductInfo(vendor, product, version), file_path) + ScanInfo(ProductInfo(vendor, product, version, location), file_path) ) else: # Add entry vendorlist.append( - ScanInfo(ProductInfo("UNKNOWN", product, version), file_path) + ScanInfo(ProductInfo("UNKNOWN", product, version, location), file_path) ) return vendorlist diff --git a/cve_bin_tool/parsers/java.py b/cve_bin_tool/parsers/java.py index 70f325e1c1..5afa89649f 100644 --- a/cve_bin_tool/parsers/java.py +++ b/cve_bin_tool/parsers/java.py @@ -31,8 +31,11 @@ def find_vendor(self, product, version): for pair in vendor_package_pair: vendor = pair["vendor"] file_path = self.filename + location = pair.get("location", "location/to/product") self.logger.debug(f"{file_path} {product} {version} by {vendor}") - info.append(ScanInfo(ProductInfo(vendor, product, version), file_path)) + info.append( + ScanInfo(ProductInfo(vendor, product, version, location), file_path) + ) return info return None diff --git a/cve_bin_tool/parsers/python.py b/cve_bin_tool/parsers/python.py index 49dc03c917..4fdddd0e7e 100644 --- a/cve_bin_tool/parsers/python.py +++ b/cve_bin_tool/parsers/python.py @@ -90,9 +90,12 @@ def run_checker(self, filename): if vendor_package_pair != []: for pair in vendor_package_pair: vendor = pair["vendor"] + location = pair.get("location", "location/to/product") file_path = self.filename self.logger.debug(f"{file_path} is {vendor}.{product} {version}") - yield ScanInfo(ProductInfo(vendor, product, version), file_path) + yield ScanInfo( + ProductInfo(vendor, product, version, location), file_path + ) # There are packages with a METADATA file in them containing different data from what the tool expects except AttributeError: diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index 7b0b588d25..cbba233843 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -4,6 +4,7 @@ from __future__ import annotations import re +import sys from collections import defaultdict from logging import Logger from pathlib import Path @@ -44,6 +45,33 @@ def __init__( # Connect to the database self.cvedb = CVEDB(version_check=False) + def find_product_location(self, product_name): + for path in sys.path: + product_location = Path(path) / product_name + if product_location.exists(): + return str(product_location) + + known_installation_directories = [ + "/usr/local/bin", + "/usr/local/sbin", + "/usr/bin", + "/opt", + "/usr/sbin", + "/usr/local/lib", + "/usr/lib", + "/usr/local/share", + "/usr/share", + "/usr/local/include", + "/usr/include", + ] + + for directory in known_installation_directories: + product_location = Path(directory) / product_name + if product_location.exists(): + return str(product_location) + + return None + def scan_file(self) -> dict[ProductInfo, TriageData]: self.logger.debug( f"Processing SBOM {self.filename} of type {self.type.upper()}" @@ -73,8 +101,14 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: # Now add vendor to create product record.... vendor_set = self.get_vendor(product) for vendor in vendor_set: + location = self.find_product_location(product) + if location is None: + location = "NotFound" + LOGGER.info(f"version_scan loca = {location}") # if vendor is not None: - parsed_data.append(ProductInfo(vendor, product, version)) + parsed_data.append( + ProductInfo(vendor, product, version, location) + ) for row in parsed_data: self.sbom_data[row]["default"] = { @@ -147,7 +181,6 @@ def parse_sbom(self): if __name__ == "__main__": - import sys file = sys.argv[1] sbom = SBOMManager(file) diff --git a/cve_bin_tool/sbom_manager/cyclonedx_parser.py b/cve_bin_tool/sbom_manager/cyclonedx_parser.py index 4ee45b245b..3482411da8 100644 --- a/cve_bin_tool/sbom_manager/cyclonedx_parser.py +++ b/cve_bin_tool/sbom_manager/cyclonedx_parser.py @@ -7,6 +7,7 @@ import defusedxml.ElementTree as ET +from cve_bin_tool.sbom_manager import SBOMManager from cve_bin_tool.validator import validate_cyclonedx @@ -38,7 +39,10 @@ def parse_cyclonedx_json(self, sbom_file: str) -> list[list[str]]: if d["type"] in self.components_supported: package = d["name"] version = d["version"] - modules.append([package, version]) + location = SBOMManager().find_product_location(package) + if location is None: + location = "NotFound" + modules.append([package, version, location]) return modules @@ -68,8 +72,10 @@ def parse_cyclonedx_xml(self, sbom_file: str) -> list[list[str]]: if component_version is None: raise KeyError(f"Could not find version in {component}") version = component_version.text - if version is not None: - modules.append([package, version]) + location = SBOMManager().find_product_location(package) + if location is None: + location = "NotFound" + modules.append([package, version, location]) return modules diff --git a/cve_bin_tool/sbom_manager/spdx_parser.py b/cve_bin_tool/sbom_manager/spdx_parser.py index 4ef399991a..435708f1ed 100644 --- a/cve_bin_tool/sbom_manager/spdx_parser.py +++ b/cve_bin_tool/sbom_manager/spdx_parser.py @@ -10,6 +10,7 @@ import yaml from cve_bin_tool.log import LOGGER +from cve_bin_tool.sbom_manager import SBOMManager from cve_bin_tool.validator import validate_spdx @@ -61,7 +62,10 @@ def parse_spdx_json(self, sbom_file: str) -> list[list[str]]: package = d["name"] try: version = d["versionInfo"] - modules.append([package, version]) + location = SBOMManager().find_product_location(package) + if location is None: + location = "NotFound" + modules.append([package, version, location]) except KeyError as e: LOGGER.debug(e, exc_info=True) diff --git a/cve_bin_tool/util.py b/cve_bin_tool/util.py index 52a7cb70d7..79269767ad 100644 --- a/cve_bin_tool/util.py +++ b/cve_bin_tool/util.py @@ -144,11 +144,13 @@ class ProductInfo(NamedTuple): vendor: str product: str version: str + location: str """ vendor: str product: str version: str + location: str class ScanInfo(NamedTuple): diff --git a/cve_bin_tool/version_scanner.py b/cve_bin_tool/version_scanner.py index bc4ee4704a..3237604270 100644 --- a/cve_bin_tool/version_scanner.py +++ b/cve_bin_tool/version_scanner.py @@ -242,11 +242,38 @@ def scan_file(self, filename: str) -> Iterator[ScanInfo]: yield from self.run_checkers(filename, lines) + def find_product_location(self, product_name): + for path in sys.path: + product_location = Path(path) / product_name + if product_location.exists(): + return str(product_location) + + known_installation_directories = [ + "/usr/local/bin", + "/usr/local/sbin", + "/usr/bin", + "/opt", + "/usr/sbin", + "/usr/local/lib", + "/usr/lib", + "/usr/local/share", + "/usr/share", + "/usr/local/include", + "/usr/include", + ] + + for directory in known_installation_directories: + product_location = Path(directory) / product_name + if product_location.exists(): + return str(product_location) + + return None + def run_checkers(self, filename: str, lines: str) -> Iterator[ScanInfo]: """process a Set of checker objects, run them on file lines, and yield information about detected products and versions. It uses logging to provide debug and error information along the way.""" - + LOGGER.info(f"filename = {filename}") # tko for dummy_checker_name, checker in self.checkers.items(): checker = checker() @@ -277,8 +304,12 @@ def run_checkers(self, filename: str, lines: str) -> Iterator[ScanInfo]: f'{file_path} {result["is_or_contains"]} {dummy_checker_name} {version}' ) for vendor, product in checker.VENDOR_PRODUCT: + location = self.find_product_location(product) + if location is None: + location = file_path yield ScanInfo( - ProductInfo(vendor, product, version), file_path + ProductInfo(vendor, product, version, location), + file_path, ) self.logger.debug(f"Done scanning file: {filename}") diff --git a/test/test_available_fix.py b/test/test_available_fix.py index 07f9809f37..26b23cfc93 100644 --- a/test/test_available_fix.py +++ b/test/test_available_fix.py @@ -152,7 +152,12 @@ def test_redhat_available_fix_output( assert expected_output == [rec.message for rec in caplog.records] MOCK_PSPP_CVE_DATA = { - ProductInfo(vendor="gnu", product="pspp", version="1.2.0"): CVEData( + ProductInfo( + vendor="gnu", + product="pspp", + version="1.2.0", + location="location/to/product", + ): CVEData( None, { "cves": [ @@ -176,7 +181,12 @@ def test_redhat_available_fix_output( } MOCK_AVAHI_CVE_DATA = { - ProductInfo(vendor="avahi", product="avahi", version="0.6.25"): CVEData( + ProductInfo( + vendor="avahi", + product="avahi", + version="0.6.25", + location="location/to/product", + ): CVEData( None, { "cves": [ @@ -221,7 +231,12 @@ def test_redhat_available_fix_output( } MOCK_NODEJS_CVE_DATA = { - ProductInfo(vendor="nodejs", product="node.js", version="14.16.0"): CVEData( + ProductInfo( + vendor="nodejs", + product="node.js", + version="14.16.0", + location="location/to/product", + ): CVEData( None, { "cves": [ diff --git a/test/test_exploits.py b/test/test_exploits.py index f6c752a5ec..688e1a5f6f 100644 --- a/test/test_exploits.py +++ b/test/test_exploits.py @@ -15,7 +15,10 @@ class TestExploitScanner: True, ["CVE-2018-19664"], ProductInfo( - vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1" + vendor="libjpeg-turbo", + product="libjpeg-turbo", + version="2.0.1", + location="location/to/product", ), { "CVE-2018-19664": { @@ -29,7 +32,10 @@ class TestExploitScanner: False, ["CVE-2018-19664"], ProductInfo( - vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1" + vendor="libjpeg-turbo", + product="libjpeg-turbo", + version="2.0.1", + location="location/to/product", ), { "CVE-2018-19664": { @@ -43,7 +49,10 @@ class TestExploitScanner: True, ["CVE-2019-19664"], ProductInfo( - vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1" + vendor="libjpeg-turbo", + product="libjpeg-turbo", + version="2.0.1", + location="location/to/product", ), { "CVE-2018-19664": { @@ -57,7 +66,10 @@ class TestExploitScanner: False, ["CVE-2019-19664"], ProductInfo( - vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1" + vendor="libjpeg-turbo", + product="libjpeg-turbo", + version="2.0.1", + location="location/to/product", ), { "CVE-2018-19664": { diff --git a/test/test_html.py b/test/test_html.py index 425017069f..2cedcc5409 100644 --- a/test/test_html.py +++ b/test/test_html.py @@ -14,7 +14,7 @@ class TestOutputHTML: MOCK_OUTPUT = { - ProductInfo("vendor0", "product0", "1.0"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1000", @@ -37,7 +37,7 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.6"): CVEData( + ProductInfo("vendor0", "product0", "2.8.6", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1002", @@ -51,7 +51,7 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor1", "product1", "3.2.1.0"): CVEData( + ProductInfo("vendor1", "product1", "3.2.1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1003", @@ -65,7 +65,7 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor1", "product1", "4.2.1.0"): CVEData( + ProductInfo("vendor1", "product1", "4.2.1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1004", @@ -79,7 +79,7 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor1", "product2", "5.2.1.0"): CVEData( + ProductInfo("vendor1", "product2", "5.2.1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1005", @@ -93,7 +93,7 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor1", "product3", "6.2.1.0"): CVEData( + ProductInfo("vendor1", "product3", "6.2.1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1006", diff --git a/test/test_input_engine.py b/test/test_input_engine.py index a8d7355f83..87a4eda9eb 100644 --- a/test/test_input_engine.py +++ b/test/test_input_engine.py @@ -25,15 +25,15 @@ class TestInputEngine: JSON_PATH = TMP_DIR / "json" VEX_PATH = TMP_DIR / "vex" PARSED_TRIAGE_DATA = { - ProductInfo("haxx", "curl", "7.59.0"): { + ProductInfo("haxx", "curl", "7.59.0", "location/to/product"): { "default": {"comments": "", "remarks": Remarks.NewFound, "severity": ""}, "paths": {""}, }, - ProductInfo("haxx", "libcurl", "7.59.0"): { + ProductInfo("haxx", "libcurl", "7.59.0", "location/to/product"): { "default": {"comments": "", "remarks": Remarks.Unexplored, "severity": ""}, "paths": {""}, }, - ProductInfo("libjpeg-turbo", "libjpeg-turbo", "2.0.1"): { + ProductInfo("libjpeg-turbo", "libjpeg-turbo", "2.0.1", "location/to/product"): { "CVE-2018-19664": { "comments": "High priority need to resolve fast", "remarks": Remarks.Confirmed, @@ -46,21 +46,21 @@ class TestInputEngine: }, "paths": {""}, }, - ProductInfo("mit", "kerberos_5", "1.15.1"): { + ProductInfo("mit", "kerberos_5", "1.15.1", "location/to/product"): { "default": {"comments": "", "remarks": Remarks.Confirmed, "severity": ""}, "paths": {""}, }, - ProductInfo("ssh", "ssh2", "2.0"): { + ProductInfo("ssh", "ssh2", "2.0", "location/to/product"): { "default": {"comments": "", "remarks": Remarks.Mitigated, "severity": ""}, "paths": {""}, }, - ProductInfo("sun", "sunos", "5.4"): { + ProductInfo("sun", "sunos", "5.4", "location/to/product"): { "default": {"comments": "", "remarks": Remarks.Mitigated, "severity": ""}, "paths": {""}, }, } VEX_TRIAGE_DATA = { - ProductInfo("d.r.commander", "libjpeg-turbo", "2.0.1"): { + ProductInfo("d.r.commander", "libjpeg-turbo", "2.0.1", "location/to/product"): { "CVE-2018-19664": { "comments": "High priority need to resolve fast", "remarks": Remarks.Confirmed, @@ -70,7 +70,7 @@ class TestInputEngine: }, "paths": {}, }, - ProductInfo("gnu", "glibc", "2.33"): { + ProductInfo("gnu", "glibc", "2.33", "location/to/product"): { "CVE-2021-1234": { "comments": "", "remarks": Remarks.Unexplored, @@ -82,7 +82,7 @@ class TestInputEngine: } # cyclonedx currently doesn't have vendors VEX_TRIAGE_DATA_CYCLONEDX = { - ProductInfo("UNKNOWN", "libjpeg-turbo", "2.0.1"): { + ProductInfo("UNKNOWN", "libjpeg-turbo", "2.0.1", "location/to/product"): { "CVE-2018-19664": { "comments": "High priority need to resolve fast", "remarks": Remarks.Confirmed, @@ -91,7 +91,7 @@ class TestInputEngine: }, "paths": {}, }, - ProductInfo("UNKNOWN", "glibc", "2.33"): { + ProductInfo("UNKNOWN", "glibc", "2.33", "location/to/product"): { "CVE-2021-1234": { "comments": "", "remarks": Remarks.Unexplored, @@ -102,7 +102,12 @@ class TestInputEngine: }, } VEX_TRIAGE_DATA_CYCLONEDX_CASE13 = { - ProductInfo(vendor="UNKNOWN", product="acme-product", version="1"): { + ProductInfo( + vendor="UNKNOWN", + product="acme-product", + version="1", + location="location/to/product", + ): { "CVE-2020-25649": { "comments": "Automated " "dataflow " @@ -130,7 +135,12 @@ class TestInputEngine: }, "paths": {}, }, - ProductInfo(vendor="UNKNOWN", product="acme-product", version="2"): { + ProductInfo( + vendor="UNKNOWN", + product="acme-product", + version="2", + location="location/to/product", + ): { "CVE-2020-25649": { "comments": "Automated " "dataflow " @@ -158,7 +168,12 @@ class TestInputEngine: }, "paths": {}, }, - ProductInfo(vendor="UNKNOWN", product="acme-product", version="3"): { + ProductInfo( + vendor="UNKNOWN", + product="acme-product", + version="3", + location="location/to/product", + ): { "CVE-2020-25649": { "comments": "Automated " "dataflow " @@ -247,7 +262,12 @@ def test_missing_fields(self, filepath, missing_fields): ) def test_valid_file(self, filepath, parsed_data): input_engine = InputEngine(filepath, error_mode=ErrorMode.FullTrace) - assert dict(input_engine.parse_input()) == parsed_data + parsed_data_actual = input_engine.parse_input() + + for product_info, expected_data in parsed_data.items(): + print("Parsed Data Actual:", parsed_data_actual) + print("Expected Data:", parsed_data) + assert parsed_data_actual[product_info] == expected_data @pytest.mark.parametrize( "filepath, parsed_data", diff --git a/test/test_language_scanner.py b/test/test_language_scanner.py index 2c890b49bb..917ef4e5f8 100644 --- a/test/test_language_scanner.py +++ b/test/test_language_scanner.py @@ -252,5 +252,7 @@ def test_python_package(self, filename: str) -> None: for product in scanner.scan_file(filename): if product: product_info, file_path = product - assert product_info == ProductInfo("facebook", "zstandard", "0.18.0") + assert product_info == ProductInfo( + "facebook", "zstandard", "0.18.0", "location/to/product" + ) assert file_path == filename diff --git a/test/test_merge.py b/test/test_merge.py index c48cc2cf0a..e7e8b1f051 100644 --- a/test/test_merge.py +++ b/test/test_merge.py @@ -21,7 +21,12 @@ class TestMergeReports: INTERMEDIATE_PATH = Path(__file__).parent.resolve() / "json" MERGED_TRIAGE_DATA = { - ProductInfo(vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1"): { + ProductInfo( + vendor="libjpeg-turbo", + product="libjpeg-turbo", + version="2.0.1", + location="location/to/product", + ): { "CVE-2018-19664": { "remarks": Remarks.Confirmed, "comments": "High priority need to resolve fast", diff --git a/test/test_output_engine.py b/test/test_output_engine.py index 1f35e2eca2..5013192dda 100644 --- a/test/test_output_engine.py +++ b/test/test_output_engine.py @@ -35,7 +35,7 @@ class TestOutputEngine(unittest.TestCase): """Test the OutputEngine class functions""" MOCK_DETAILED_OUTPUT = { - ProductInfo("vendor0", "product0", "1.0"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1000", @@ -52,7 +52,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.6"): CVEData( + ProductInfo("vendor0", "product0", "2.8.6", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1001", @@ -74,6 +74,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-1234-1000", "source": "NVD", "severity": "MEDIUM", @@ -91,6 +92,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "2.8.6", + "location": "location/to/product", "cve_number": "CVE-1234-1001", "source": "NVD", "severity": "LOW", @@ -107,7 +109,7 @@ class TestOutputEngine(unittest.TestCase): ] MOCK_OUTPUT = { - ProductInfo("vendor0", "product0", "1.0"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1004", @@ -156,7 +158,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.6"): CVEData( + ProductInfo("vendor0", "product0", "2.8.6", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1007", @@ -201,7 +203,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor1", "product1", "3.2.1.0"): CVEData( + ProductInfo("vendor1", "product1", "3.2.1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1010", @@ -225,7 +227,7 @@ class TestOutputEngine(unittest.TestCase): } MOCK_OUTPUT_2 = { - ProductInfo("vendor0", "product0", "1.0"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1011", @@ -248,7 +250,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.7"): CVEData( + ProductInfo("vendor0", "product0", "2.8.7", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1013", @@ -262,7 +264,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor1", "product1", "3.3.1"): CVEData( + ProductInfo("vendor1", "product1", "3.3.1", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1014", @@ -279,7 +281,7 @@ class TestOutputEngine(unittest.TestCase): } MOCK_PDF_OUTPUT = { - ProductInfo("vendor0", "product0", "1.0"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1015", @@ -306,7 +308,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.6"): CVEData( + ProductInfo("vendor0", "product0", "2.8.6", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1017", @@ -322,7 +324,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor1", "product1", "3.2.1.0"): CVEData( + ProductInfo("vendor1", "product1", "3.2.1.0", "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1018", @@ -339,7 +341,7 @@ class TestOutputEngine(unittest.TestCase): } MOCK_ALL_CVE_DATA = { - ProductInfo("vendor0", "product0", "1.0"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( cves=[ CVE( "UNKNOWN", @@ -471,6 +473,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-1234-1004", "severity": "CRITICAL", "score": "4.2", @@ -487,6 +490,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-1234-1005", "severity": "MEDIUM", "score": "4.2", @@ -505,6 +509,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-1234-1006", "severity": "LOW", "score": "1.2", @@ -523,6 +528,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "2.8.6", + "location": "location/to/product", "cve_number": "CVE-1234-1007", "severity": "LOW", "score": "2.5", @@ -539,6 +545,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "2.8.6", + "location": "location/to/product", "cve_number": "CVE-1234-1008", "severity": "UNKNOWN", "score": "2.5", @@ -555,6 +562,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "2.8.6", + "location": "location/to/product", "cve_number": "CVE-1234-1009", "severity": "MEDIUM", "score": "2.5", @@ -571,6 +579,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor1", "product": "product1", "version": "3.2.1.0", + "location": "location/to/product", "cve_number": "CVE-1234-1010", "severity": "HIGH", "score": "7.5", @@ -590,6 +599,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "UNKNOWN", "severity": "UNKNOWN", "score": "0", @@ -604,6 +614,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-0001", "severity": "MEDIUM", "score": "4.2", @@ -618,6 +629,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-0002", "severity": "MEDIUM", "score": "4.2", @@ -632,6 +644,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-0003", "severity": "MEDIUM", "score": "4.2", @@ -646,6 +659,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-0004", "severity": "MEDIUM", "score": "4.2", @@ -660,6 +674,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-0005", "severity": "MEDIUM", "score": "4.2", @@ -674,6 +689,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-0006", "severity": "MEDIUM", "score": "4.2", @@ -688,6 +704,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-0007", "severity": "MEDIUM", "score": "4.2", @@ -702,6 +719,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-0008", "severity": "MEDIUM", "score": "4.2", @@ -716,6 +734,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-9999-9999", "severity": "LOW", "score": "1.2", @@ -1164,7 +1183,7 @@ def test_output_vex_urn_cbt(self): """Test that versions are fully captured when encoding a URN""" version = "sky%2fx6069_trx_l601_sky%2fx6069_trx_l601_sky%3a6.0%2fmra58k%2f1482897127%3auser%2frelease-keys" mocked_version_output = { - ProductInfo("vendor0", "product0", version): CVEData( + ProductInfo("vendor0", "product0", version, "location/to/product"): CVEData( cves=[ CVE( "CVE-1234-1018", @@ -1551,7 +1570,9 @@ def test_csv_macros(self): characters, used in spreadsheet macros""" bad_input = { - ProductInfo("=vendor0", "\t+product0", "@1.0"): CVEData( + ProductInfo( + "=vendor0", "\t+product0", "@1.0", "location/to/product" + ): CVEData( cves=[ CVE( "-CVE-1234-1234", @@ -1573,6 +1594,7 @@ def test_csv_macros(self): "vendor": "vendor0", "product": "product0", "version": "1.0", + "location": "location/to/product", "cve_number": "CVE-1234-1234", "source": "NVD", "severity": "MEDIUM", diff --git a/test/test_package_list_parser.py b/test/test_package_list_parser.py index 2d5d47f635..dcedbc7f76 100644 --- a/test/test_package_list_parser.py +++ b/test/test_package_list_parser.py @@ -27,11 +27,21 @@ class TestPackageListParser: TXT_PATH = Path(__file__).parent.resolve() / "txt" REQ_PARSED_TRIAGE_DATA = { - ProductInfo(vendor="httplib2_project*", product="httplib2", version="0.18.1"): { + ProductInfo( + vendor="httplib2_project*", + product="httplib2", + version="0.18.1", + location="location/to/product", + ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, - ProductInfo(vendor="python*", product="requests", version="2.25.1"): { + ProductInfo( + vendor="python*", + product="requests", + version="2.25.1", + location="location/to/product", + ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, @@ -60,19 +70,28 @@ class TestPackageListParser: UBUNTU_PARSED_TRIAGE_DATA = { ProductInfo( - vendor="gnu*", product="bash", version=UBUNTU_PACKAGE_VERSIONS[0] + vendor="gnu*", + product="bash", + version=UBUNTU_PACKAGE_VERSIONS[0], + location="location/to/product", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, ProductInfo( - vendor="gnu*", product="binutils", version=UBUNTU_PACKAGE_VERSIONS[1] + vendor="gnu*", + product="binutils", + version=UBUNTU_PACKAGE_VERSIONS[1], + location="location/to/product", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, ProductInfo( - vendor="gnu*", product="wget", version=UBUNTU_PACKAGE_VERSIONS[2] + vendor="gnu*", + product="wget", + version=UBUNTU_PACKAGE_VERSIONS[2], + location="location/to/product", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, diff --git a/test/test_sbom.py b/test/test_sbom.py index 4020ed0a41..bcead26414 100644 --- a/test/test_sbom.py +++ b/test/test_sbom.py @@ -15,24 +15,32 @@ class TestSBOM: SBOM_PATH = Path(__file__).parent.resolve() / "sbom" PARSED_SBOM_DATA = { - ProductInfo(vendor="gnu", product="glibc", version="2.11.1"): { + ProductInfo( + vendor="gnu", product="glibc", version="2.11.1", location="NotFound" + ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, } } PARSED_SBOM_DATA2 = { - ProductInfo(vendor="ubuntu", product="ubuntu", version="22.04"): { + ProductInfo( + vendor="ubuntu", product="ubuntu", version="22.04", location="NotFound" + ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, } } PARSED_SBOM_DATA3 = { - ProductInfo(vendor="gnu", product="glibc", version="2.11.1"): { + ProductInfo( + vendor="gnu", product="glibc", version="2.11.1", location="NotFound" + ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, - ProductInfo(vendor="saxon", product="saxon", version="8.8"): { + ProductInfo( + vendor="saxon", product="saxon", version="8.8", location="NotFound" + ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, From bdaf4acf40a6a33e0d61f119dc00b063cc13f7e3 Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Sun, 31 Mar 2024 16:29:19 +0530 Subject: [PATCH 02/15] feat: cleaner organization for code --- cve_bin_tool/sbom_manager/__init__.py | 31 ++-------------------- cve_bin_tool/util.py | 28 ++++++++++++++++++++ cve_bin_tool/version_scanner.py | 37 ++++++--------------------- 3 files changed, 38 insertions(+), 58 deletions(-) diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index cbba233843..fb2c34abff 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -16,7 +16,7 @@ from cve_bin_tool.cvedb import CVEDB from cve_bin_tool.input_engine import TriageData from cve_bin_tool.log import LOGGER -from cve_bin_tool.util import ProductInfo, Remarks +from cve_bin_tool.util import ProductInfo, Remarks, find_product_location from cve_bin_tool.validator import validate_cyclonedx, validate_spdx from .swid_parser import SWIDParser @@ -45,33 +45,6 @@ def __init__( # Connect to the database self.cvedb = CVEDB(version_check=False) - def find_product_location(self, product_name): - for path in sys.path: - product_location = Path(path) / product_name - if product_location.exists(): - return str(product_location) - - known_installation_directories = [ - "/usr/local/bin", - "/usr/local/sbin", - "/usr/bin", - "/opt", - "/usr/sbin", - "/usr/local/lib", - "/usr/lib", - "/usr/local/share", - "/usr/share", - "/usr/local/include", - "/usr/include", - ] - - for directory in known_installation_directories: - product_location = Path(directory) / product_name - if product_location.exists(): - return str(product_location) - - return None - def scan_file(self) -> dict[ProductInfo, TriageData]: self.logger.debug( f"Processing SBOM {self.filename} of type {self.type.upper()}" @@ -101,7 +74,7 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: # Now add vendor to create product record.... vendor_set = self.get_vendor(product) for vendor in vendor_set: - location = self.find_product_location(product) + location = find_product_location(product) if location is None: location = "NotFound" LOGGER.info(f"version_scan loca = {location}") diff --git a/cve_bin_tool/util.py b/cve_bin_tool/util.py index 79269767ad..097ba96071 100644 --- a/cve_bin_tool/util.py +++ b/cve_bin_tool/util.py @@ -276,6 +276,34 @@ def make_http_requests(attribute, **kwargs): LOGGER.error(ve) +def find_product_location(self, product_name): + for path in sys.path: + product_location = Path(path) / product_name + if product_location.exists(): + return str(product_location) + + known_installation_directories = [ + "/usr/local/bin", + "/usr/local/sbin", + "/usr/bin", + "/opt", + "/usr/sbin", + "/usr/local/lib", + "/usr/lib", + "/usr/local/share", + "/usr/share", + "/usr/local/include", + "/usr/include", + ] + + for directory in known_installation_directories: + product_location = Path(directory) / product_name + if product_location.exists(): + return str(product_location) + + return None + + class DirWalk: """ for filename in DirWalk('*.c').walk(roots): diff --git a/cve_bin_tool/version_scanner.py b/cve_bin_tool/version_scanner.py index 3237604270..6e9cf1052f 100644 --- a/cve_bin_tool/version_scanner.py +++ b/cve_bin_tool/version_scanner.py @@ -17,7 +17,13 @@ from cve_bin_tool.log import LOGGER from cve_bin_tool.parsers.parse import parse, valid_files from cve_bin_tool.strings import parse_strings -from cve_bin_tool.util import DirWalk, ProductInfo, ScanInfo, inpath +from cve_bin_tool.util import ( + DirWalk, + ProductInfo, + ScanInfo, + find_product_location, + inpath, +) if sys.version_info >= (3, 10): from importlib import metadata as importlib_metadata @@ -242,33 +248,6 @@ def scan_file(self, filename: str) -> Iterator[ScanInfo]: yield from self.run_checkers(filename, lines) - def find_product_location(self, product_name): - for path in sys.path: - product_location = Path(path) / product_name - if product_location.exists(): - return str(product_location) - - known_installation_directories = [ - "/usr/local/bin", - "/usr/local/sbin", - "/usr/bin", - "/opt", - "/usr/sbin", - "/usr/local/lib", - "/usr/lib", - "/usr/local/share", - "/usr/share", - "/usr/local/include", - "/usr/include", - ] - - for directory in known_installation_directories: - product_location = Path(directory) / product_name - if product_location.exists(): - return str(product_location) - - return None - def run_checkers(self, filename: str, lines: str) -> Iterator[ScanInfo]: """process a Set of checker objects, run them on file lines, and yield information about detected products and versions. @@ -304,7 +283,7 @@ def run_checkers(self, filename: str, lines: str) -> Iterator[ScanInfo]: f'{file_path} {result["is_or_contains"]} {dummy_checker_name} {version}' ) for vendor, product in checker.VENDOR_PRODUCT: - location = self.find_product_location(product) + location = find_product_location(product) if location is None: location = file_path yield ScanInfo( From 3abd312212a077f19e245cb4343597227fefd9d1 Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Sun, 31 Mar 2024 16:37:01 +0530 Subject: [PATCH 03/15] feat: improvement --- cve_bin_tool/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cve_bin_tool/util.py b/cve_bin_tool/util.py index 097ba96071..7bdd0d9ccb 100644 --- a/cve_bin_tool/util.py +++ b/cve_bin_tool/util.py @@ -276,7 +276,7 @@ def make_http_requests(attribute, **kwargs): LOGGER.error(ve) -def find_product_location(self, product_name): +def find_product_location(product_name): for path in sys.path: product_location = Path(path) / product_name if product_location.exists(): From 4dd77ffdb640b9b0dac0ee30a82dbc329b02a336 Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Sun, 31 Mar 2024 16:37:01 +0530 Subject: [PATCH 04/15] feat: improvement --- cve_bin_tool/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cve_bin_tool/util.py b/cve_bin_tool/util.py index 097ba96071..7bdd0d9ccb 100644 --- a/cve_bin_tool/util.py +++ b/cve_bin_tool/util.py @@ -276,7 +276,7 @@ def make_http_requests(attribute, **kwargs): LOGGER.error(ve) -def find_product_location(self, product_name): +def find_product_location(product_name): for path in sys.path: product_location = Path(path) / product_name if product_location.exists(): From 567573de079b2a421c3e26b81d305d9fc78fe9f0 Mon Sep 17 00:00:00 2001 From: Mayankrai449 Date: Mon, 1 Apr 2024 00:23:40 +0530 Subject: [PATCH 05/15] test: update From eacbbc609403b032f2503bc15e8cf05b4a4e315f Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Mon, 1 Apr 2024 19:31:06 +0530 Subject: [PATCH 06/15] test: update --- cve_bin_tool/sbom_manager/__init__.py | 1 - cve_bin_tool/sbom_manager/cyclonedx_parser.py | 6 +++--- cve_bin_tool/sbom_manager/spdx_parser.py | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index fb2c34abff..8866a640a6 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -77,7 +77,6 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: location = find_product_location(product) if location is None: location = "NotFound" - LOGGER.info(f"version_scan loca = {location}") # if vendor is not None: parsed_data.append( ProductInfo(vendor, product, version, location) diff --git a/cve_bin_tool/sbom_manager/cyclonedx_parser.py b/cve_bin_tool/sbom_manager/cyclonedx_parser.py index 3482411da8..fbfe1e0130 100644 --- a/cve_bin_tool/sbom_manager/cyclonedx_parser.py +++ b/cve_bin_tool/sbom_manager/cyclonedx_parser.py @@ -7,7 +7,7 @@ import defusedxml.ElementTree as ET -from cve_bin_tool.sbom_manager import SBOMManager +from cve_bin_tool.util import find_product_location from cve_bin_tool.validator import validate_cyclonedx @@ -39,7 +39,7 @@ def parse_cyclonedx_json(self, sbom_file: str) -> list[list[str]]: if d["type"] in self.components_supported: package = d["name"] version = d["version"] - location = SBOMManager().find_product_location(package) + location = find_product_location(package) if location is None: location = "NotFound" modules.append([package, version, location]) @@ -72,7 +72,7 @@ def parse_cyclonedx_xml(self, sbom_file: str) -> list[list[str]]: if component_version is None: raise KeyError(f"Could not find version in {component}") version = component_version.text - location = SBOMManager().find_product_location(package) + location = find_product_location(package) if location is None: location = "NotFound" modules.append([package, version, location]) diff --git a/cve_bin_tool/sbom_manager/spdx_parser.py b/cve_bin_tool/sbom_manager/spdx_parser.py index 435708f1ed..6ab31e7dc8 100644 --- a/cve_bin_tool/sbom_manager/spdx_parser.py +++ b/cve_bin_tool/sbom_manager/spdx_parser.py @@ -10,7 +10,7 @@ import yaml from cve_bin_tool.log import LOGGER -from cve_bin_tool.sbom_manager import SBOMManager +from cve_bin_tool.util import find_product_location from cve_bin_tool.validator import validate_spdx @@ -62,7 +62,7 @@ def parse_spdx_json(self, sbom_file: str) -> list[list[str]]: package = d["name"] try: version = d["versionInfo"] - location = SBOMManager().find_product_location(package) + location = find_product_location(package) if location is None: location = "NotFound" modules.append([package, version, location]) From 5bb03d06b4cfe51b7b9c544e81a7f4eb30dafbd6 Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Thu, 4 Apr 2024 03:01:46 +0530 Subject: [PATCH 07/15] test: new test and validation update --- cve_bin_tool/output_engine/util.py | 2 +- cve_bin_tool/parsers/__init__.py | 2 +- cve_bin_tool/parsers/java.py | 2 +- cve_bin_tool/parsers/python.py | 2 +- cve_bin_tool/sbom_manager/__init__.py | 11 +++- cve_bin_tool/util.py | 15 ++++++ cve_bin_tool/version_scanner.py | 5 ++ requirements.txt | 2 +- test/test_available_fix.py | 6 +-- test/test_exploits.py | 8 +-- test/test_html.py | 20 +++++--- test/test_language_scanner.py | 2 +- test/test_output_engine.py | 74 +++++++++++++++------------ test/test_package_list_parser.py | 10 ++-- test/test_util.py | 21 +++++++- 15 files changed, 122 insertions(+), 60 deletions(-) diff --git a/cve_bin_tool/output_engine/util.py b/cve_bin_tool/output_engine/util.py index 62a758e0e5..ae1c8dad91 100644 --- a/cve_bin_tool/output_engine/util.py +++ b/cve_bin_tool/output_engine/util.py @@ -160,7 +160,7 @@ def format_output( "vendor": "haxx" "product": "curl", "version": "1.2.1", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1234", "severity": "LOW", "score": "1.2", diff --git a/cve_bin_tool/parsers/__init__.py b/cve_bin_tool/parsers/__init__.py index 6963e2f2cc..c3ac1fc6ed 100644 --- a/cve_bin_tool/parsers/__init__.py +++ b/cve_bin_tool/parsers/__init__.py @@ -67,7 +67,7 @@ def find_vendor(self, product, version): # To handle multiple vendors, return all combinations of product/vendor mappings for v in vendor_package_pair: vendor = v["vendor"] - location = v.get("location", "location/to/product") + location = v.get("location", "/usr/local/bin/product") self.logger.debug(f"{file_path} {product} {version} by {vendor}") vendorlist.append( ScanInfo(ProductInfo(vendor, product, version, location), file_path) diff --git a/cve_bin_tool/parsers/java.py b/cve_bin_tool/parsers/java.py index 384a40bd87..9cb22cc6e8 100644 --- a/cve_bin_tool/parsers/java.py +++ b/cve_bin_tool/parsers/java.py @@ -57,7 +57,7 @@ def find_vendor(self, product, version): for pair in vendor_package_pair: vendor = pair["vendor"] file_path = self.filename - location = pair.get("location", "location/to/product") + location = pair.get("location", "/usr/local/bin/product") self.logger.debug(f"{file_path} {product} {version} by {vendor}") info.append( ScanInfo(ProductInfo(vendor, product, version, location), file_path) diff --git a/cve_bin_tool/parsers/python.py b/cve_bin_tool/parsers/python.py index 51970b53a8..ff8f12fe78 100644 --- a/cve_bin_tool/parsers/python.py +++ b/cve_bin_tool/parsers/python.py @@ -152,7 +152,7 @@ def run_checker(self, filename): if vendor_package_pair != []: for pair in vendor_package_pair: vendor = pair["vendor"] - location = pair.get("location", "location/to/product") + location = pair.get("location", "/usr/local/bin/product") file_path = self.filename self.logger.debug(f"{file_path} is {vendor}.{product} {version}") yield ScanInfo( diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index f71c1ee696..c70b83b6fb 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -16,7 +16,12 @@ from cve_bin_tool.cvedb import CVEDB from cve_bin_tool.input_engine import TriageData from cve_bin_tool.log import LOGGER -from cve_bin_tool.util import ProductInfo, Remarks, find_product_location +from cve_bin_tool.util import ( + ProductInfo, + Remarks, + find_product_location, + validate_location, +) from cve_bin_tool.validator import validate_cyclonedx, validate_spdx from .swid_parser import SWIDParser @@ -127,6 +132,10 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: location = find_product_location(product) if location is None: location = "NotFound" + if validate_location(location) is False: + raise ValueError( + f"Invalid location {location} for {product}" + ) # if vendor is not None: parsed_data.append( ProductInfo(vendor, product, version, location) diff --git a/cve_bin_tool/util.py b/cve_bin_tool/util.py index 721f997997..ef64b6c2c4 100644 --- a/cve_bin_tool/util.py +++ b/cve_bin_tool/util.py @@ -6,6 +6,7 @@ import fnmatch import os +import re import sys from enum import Enum from pathlib import Path @@ -290,6 +291,11 @@ def find_product_location(product_name): product_location = Path(path) / product_name if product_location.exists(): return str(product_location) + parts = product_name.split("-") + for part in parts: + product_location = Path(path) / part + if product_location.exists(): + return str(product_location) known_installation_directories = [ "/usr/local/bin", @@ -313,6 +319,15 @@ def find_product_location(product_name): return None +def validate_location(location: str) -> bool: + """ + Validates the location. + Returns True if the location is valid, False otherwise. + """ + pattern = r"^(?!https?:\/\/)(?=.*\/)(?!.*@)[a-zA-Z0-9_\-\/\s]+|NotFound$" + return bool(re.match(pattern, location)) + + class DirWalk: """ for filename in DirWalk('*.c').walk(roots): diff --git a/cve_bin_tool/version_scanner.py b/cve_bin_tool/version_scanner.py index 6e9cf1052f..be26446624 100644 --- a/cve_bin_tool/version_scanner.py +++ b/cve_bin_tool/version_scanner.py @@ -23,6 +23,7 @@ ScanInfo, find_product_location, inpath, + validate_location, ) if sys.version_info >= (3, 10): @@ -286,6 +287,10 @@ def run_checkers(self, filename: str, lines: str) -> Iterator[ScanInfo]: location = find_product_location(product) if location is None: location = file_path + if validate_location(location) is False: + raise ValueError( + f"Invalid location {location} for {product}" + ) yield ScanInfo( ProductInfo(vendor, product, version, location), file_path, diff --git a/requirements.txt b/requirements.txt index c48ef39f99..728d266f7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ importlib_metadata>=3.6; python_version < "3.10" importlib_resources; python_version < "3.9" jinja2>=2.11.3 jsonschema>=3.0.2 -lib4sbom>=0.5.0 +lib4sbom>=0.7.0 python-gnupg packageurl-python packaging diff --git a/test/test_available_fix.py b/test/test_available_fix.py index 26b23cfc93..9ada02a206 100644 --- a/test/test_available_fix.py +++ b/test/test_available_fix.py @@ -156,7 +156,7 @@ def test_redhat_available_fix_output( vendor="gnu", product="pspp", version="1.2.0", - location="location/to/product", + location="/usr/local/bin/pspp", ): CVEData( None, { @@ -185,7 +185,7 @@ def test_redhat_available_fix_output( vendor="avahi", product="avahi", version="0.6.25", - location="location/to/product", + location="/usr/local/bin/avahi", ): CVEData( None, { @@ -235,7 +235,7 @@ def test_redhat_available_fix_output( vendor="nodejs", product="node.js", version="14.16.0", - location="location/to/product", + location="/usr/local/bin/nodejs", ): CVEData( None, { diff --git a/test/test_exploits.py b/test/test_exploits.py index 688e1a5f6f..348ba5c2e8 100644 --- a/test/test_exploits.py +++ b/test/test_exploits.py @@ -18,7 +18,7 @@ class TestExploitScanner: vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1", - location="location/to/product", + location="/usr/lib/x86_64-linux-gnu/libjpeg-turbo", ), { "CVE-2018-19664": { @@ -35,7 +35,7 @@ class TestExploitScanner: vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1", - location="location/to/product", + location="/usr/lib/x86_64-linux-gnu/libjpeg-turbo", ), { "CVE-2018-19664": { @@ -52,7 +52,7 @@ class TestExploitScanner: vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1", - location="location/to/product", + location="/usr/lib/x86_64-linux-gnu/libjpeg-turbo", ), { "CVE-2018-19664": { @@ -69,7 +69,7 @@ class TestExploitScanner: vendor="libjpeg-turbo", product="libjpeg-turbo", version="2.0.1", - location="location/to/product", + location="/usr/lib/x86_64-linux-gnu/libjpeg-turbo", ), { "CVE-2018-19664": { diff --git a/test/test_html.py b/test/test_html.py index 2cedcc5409..8165030e1c 100644 --- a/test/test_html.py +++ b/test/test_html.py @@ -14,7 +14,7 @@ class TestOutputHTML: MOCK_OUTPUT = { - ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1000", @@ -37,7 +37,7 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.6", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "2.8.6", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1002", @@ -51,7 +51,9 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor1", "product1", "3.2.1.0", "location/to/product"): CVEData( + ProductInfo( + "vendor1", "product1", "3.2.1.0", "/usr/local/bin/product" + ): CVEData( cves=[ CVE( "CVE-1234-1003", @@ -65,7 +67,9 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor1", "product1", "4.2.1.0", "location/to/product"): CVEData( + ProductInfo( + "vendor1", "product1", "4.2.1.0", "/usr/local/bin/product" + ): CVEData( cves=[ CVE( "CVE-1234-1004", @@ -79,7 +83,9 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor1", "product2", "5.2.1.0", "location/to/product"): CVEData( + ProductInfo( + "vendor1", "product2", "5.2.1.0", "/usr/local/bin/product" + ): CVEData( cves=[ CVE( "CVE-1234-1005", @@ -93,7 +99,9 @@ class TestOutputHTML: ], paths={""}, ), - ProductInfo("vendor1", "product3", "6.2.1.0", "location/to/product"): CVEData( + ProductInfo( + "vendor1", "product3", "6.2.1.0", "/usr/local/bin/product" + ): CVEData( cves=[ CVE( "CVE-1234-1006", diff --git a/test/test_language_scanner.py b/test/test_language_scanner.py index 917ef4e5f8..d30dfb325e 100644 --- a/test/test_language_scanner.py +++ b/test/test_language_scanner.py @@ -253,6 +253,6 @@ def test_python_package(self, filename: str) -> None: if product: product_info, file_path = product assert product_info == ProductInfo( - "facebook", "zstandard", "0.18.0", "location/to/product" + "facebook", "zstandard", "0.18.0", "/usr/local/bin/product" ) assert file_path == filename diff --git a/test/test_output_engine.py b/test/test_output_engine.py index 5013192dda..b294153d0f 100644 --- a/test/test_output_engine.py +++ b/test/test_output_engine.py @@ -35,7 +35,7 @@ class TestOutputEngine(unittest.TestCase): """Test the OutputEngine class functions""" MOCK_DETAILED_OUTPUT = { - ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1000", @@ -52,7 +52,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.6", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "2.8.6", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1001", @@ -74,7 +74,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1000", "source": "NVD", "severity": "MEDIUM", @@ -92,7 +92,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "2.8.6", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1001", "source": "NVD", "severity": "LOW", @@ -109,7 +109,7 @@ class TestOutputEngine(unittest.TestCase): ] MOCK_OUTPUT = { - ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1004", @@ -158,7 +158,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.6", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "2.8.6", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1007", @@ -203,7 +203,9 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor1", "product1", "3.2.1.0", "location/to/product"): CVEData( + ProductInfo( + "vendor1", "product1", "3.2.1.0", "/usr/local/bin/product" + ): CVEData( cves=[ CVE( "CVE-1234-1010", @@ -227,7 +229,7 @@ class TestOutputEngine(unittest.TestCase): } MOCK_OUTPUT_2 = { - ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1011", @@ -250,7 +252,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.7", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "2.8.7", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1013", @@ -264,7 +266,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor1", "product1", "3.3.1", "location/to/product"): CVEData( + ProductInfo("vendor1", "product1", "3.3.1", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1014", @@ -281,7 +283,7 @@ class TestOutputEngine(unittest.TestCase): } MOCK_PDF_OUTPUT = { - ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1015", @@ -308,7 +310,7 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor0", "product0", "2.8.6", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "2.8.6", "/usr/local/bin/product"): CVEData( cves=[ CVE( "CVE-1234-1017", @@ -324,7 +326,9 @@ class TestOutputEngine(unittest.TestCase): ], paths={""}, ), - ProductInfo("vendor1", "product1", "3.2.1.0", "location/to/product"): CVEData( + ProductInfo( + "vendor1", "product1", "3.2.1.0", "/usr/local/bin/product" + ): CVEData( cves=[ CVE( "CVE-1234-1018", @@ -341,7 +345,7 @@ class TestOutputEngine(unittest.TestCase): } MOCK_ALL_CVE_DATA = { - ProductInfo("vendor0", "product0", "1.0", "location/to/product"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "/usr/local/bin/product"): CVEData( cves=[ CVE( "UNKNOWN", @@ -473,7 +477,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1004", "severity": "CRITICAL", "score": "4.2", @@ -490,7 +494,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1005", "severity": "MEDIUM", "score": "4.2", @@ -509,7 +513,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1006", "severity": "LOW", "score": "1.2", @@ -528,7 +532,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "2.8.6", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1007", "severity": "LOW", "score": "2.5", @@ -545,7 +549,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "2.8.6", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1008", "severity": "UNKNOWN", "score": "2.5", @@ -562,7 +566,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "2.8.6", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1009", "severity": "MEDIUM", "score": "2.5", @@ -579,7 +583,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor1", "product": "product1", "version": "3.2.1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1010", "severity": "HIGH", "score": "7.5", @@ -599,7 +603,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "UNKNOWN", "severity": "UNKNOWN", "score": "0", @@ -614,7 +618,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-0001", "severity": "MEDIUM", "score": "4.2", @@ -629,7 +633,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-0002", "severity": "MEDIUM", "score": "4.2", @@ -644,7 +648,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-0003", "severity": "MEDIUM", "score": "4.2", @@ -659,7 +663,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-0004", "severity": "MEDIUM", "score": "4.2", @@ -674,7 +678,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-0005", "severity": "MEDIUM", "score": "4.2", @@ -689,7 +693,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-0006", "severity": "MEDIUM", "score": "4.2", @@ -704,7 +708,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-0007", "severity": "MEDIUM", "score": "4.2", @@ -719,7 +723,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-0008", "severity": "MEDIUM", "score": "4.2", @@ -734,7 +738,7 @@ class TestOutputEngine(unittest.TestCase): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-9999-9999", "severity": "LOW", "score": "1.2", @@ -1183,7 +1187,9 @@ def test_output_vex_urn_cbt(self): """Test that versions are fully captured when encoding a URN""" version = "sky%2fx6069_trx_l601_sky%2fx6069_trx_l601_sky%3a6.0%2fmra58k%2f1482897127%3auser%2frelease-keys" mocked_version_output = { - ProductInfo("vendor0", "product0", version, "location/to/product"): CVEData( + ProductInfo( + "vendor0", "product0", version, "/usr/local/bin/product" + ): CVEData( cves=[ CVE( "CVE-1234-1018", @@ -1571,7 +1577,7 @@ def test_csv_macros(self): bad_input = { ProductInfo( - "=vendor0", "\t+product0", "@1.0", "location/to/product" + "=vendor0", "\t+product0", "@1.0", "/usr/local/bin/product" ): CVEData( cves=[ CVE( @@ -1594,7 +1600,7 @@ def test_csv_macros(self): "vendor": "vendor0", "product": "product0", "version": "1.0", - "location": "location/to/product", + "location": "/usr/local/bin/product", "cve_number": "CVE-1234-1234", "source": "NVD", "severity": "MEDIUM", diff --git a/test/test_package_list_parser.py b/test/test_package_list_parser.py index dcedbc7f76..8d6ca59aee 100644 --- a/test/test_package_list_parser.py +++ b/test/test_package_list_parser.py @@ -31,7 +31,7 @@ class TestPackageListParser: vendor="httplib2_project*", product="httplib2", version="0.18.1", - location="location/to/product", + location="/usr/local/bin/httplib2", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -40,7 +40,7 @@ class TestPackageListParser: vendor="python*", product="requests", version="2.25.1", - location="location/to/product", + location="/usr/local/bin/requests", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -73,7 +73,7 @@ class TestPackageListParser: vendor="gnu*", product="bash", version=UBUNTU_PACKAGE_VERSIONS[0], - location="location/to/product", + location="/usr/local/bin/bash", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -82,7 +82,7 @@ class TestPackageListParser: vendor="gnu*", product="binutils", version=UBUNTU_PACKAGE_VERSIONS[1], - location="location/to/product", + location="/usr/local/bin/binutils", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -91,7 +91,7 @@ class TestPackageListParser: vendor="gnu*", product="wget", version=UBUNTU_PACKAGE_VERSIONS[2], - location="location/to/product", + location="/usr/local/bin/wget", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, diff --git a/test/test_util.py b/test/test_util.py index 52c5588d3d..04c7f552ef 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -5,10 +5,12 @@ CVE-bin-tool util tests """ import inspect +import sys +from pathlib import Path from typing import DefaultDict from cve_bin_tool.cve_scanner import CVEScanner -from cve_bin_tool.util import CVEData, ProductInfo, inpath +from cve_bin_tool.util import CVEData, ProductInfo, find_product_location, inpath class TestUtil: @@ -22,6 +24,23 @@ def test_inpath(self): def test_not_inpath(self): assert not inpath("cve_bin_tool_test_for_not_in_path") + def test_find_product_location(self, monkeypatch): + + mock_sys_path = ["/usr/local/bin", "/usr/local/lib/python3.10/site-packages"] + product_name = "lib4sbom" + monkeypatch.setattr(sys, "path", mock_sys_path) + + def mock_exists(path): + if "/usr/local/lib/python3.10/site-packages/lib4sbom" in str(path): + return True + return False + + monkeypatch.setattr(Path, "exists", mock_exists) + + expected_path = "/usr/local/lib/python3.10/site-packages/lib4sbom" + + assert find_product_location(product_name) == expected_path + class TestSignature: """Tests signature of critical class and functions""" From d48dec5650171168035f8b2c0e544e69d689ab8d Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Thu, 4 Apr 2024 11:34:14 +0530 Subject: [PATCH 08/15] test: test updates for new merges --- .coverage.LAPTOP-HA595TQK.23831.XejgJzQx | Bin 0 -> 53248 bytes cve_bin_tool/output_engine/__init__.py | 5 +--- test/test_html.py | 4 +-- test/test_sbom.py | 34 +++++++++++++++++++---- 4 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 .coverage.LAPTOP-HA595TQK.23831.XejgJzQx diff --git a/.coverage.LAPTOP-HA595TQK.23831.XejgJzQx b/.coverage.LAPTOP-HA595TQK.23831.XejgJzQx new file mode 100644 index 0000000000000000000000000000000000000000..63d2dd624e81b097920c3866de42c01dcaf64303 GIT binary patch literal 53248 zcmeI)?{C{g7zc1W+2STm<)Nyws%q-Jfizl@DPj@=UO<7q7)%<23SJb=bpPu>TX{@dE_NZ z48zEmiFntzW02V7eEa!^{f$wt{ASqSZ#=f! z_1`z%S^u-{SASjq!;)2(PGExo1Rwx`|F^*ON!@a`x6NliC9*$KNhDnrWzQR*es!>a zcpwh*BPUo}I zt&KweeiDk27o^P$VlVN+K&W%&P7*b^v4Jf{(;MNLHg_Ceu&SiO%FmLQD_stD`S+sYdJ2g4? zXqIadcqbDz>-?4|a<0r8DoLJXB(CgBzZzNFJ$9 zem0vHUUwi-=ya#?p&u$kH5~tpI`(4kRU({cw_`@yl=1@Z`0psBcz4@m||gs%Dy;hWD3Y z9DB5^^0F0wppS~iv5a{ZiW{OIh9f0|Y;B+%4`aHf7mKtG*H@*FiZbF0F5~F5k=98s zEZ5}}NuL4z`;A$p}uU1)xmXDnTQXG|ts zsxJrGaOX~$EAi}DT`MkIjXsl!N<3c)PLft84XiU63pY}9lGf5@qx`Zk;NI$W`I)~Z zuklB@>8@!x_wJd~Nj?~}d8QZBqV7vw?fh+NAxN&AEaK%cO}O-FXFg8YZjOWBW>?_J zE$hL4rl3>hpLJ?Y{(^7X{~Gjz4FV8=00bZa0SG_<0uX=z1Rwx`)e|V2CDY>X|0Vk` z!~UC|ut5L<5P$##AOHafKmY;|fB*y_@Ma3sOXa(^{u7U`ZI*YO{EqpbW@s8Op* z_6x&)VgK`HHW0Oj00bZa0SG_<0uX=z1Rwwb2tYs!)XR5G{ab)it-Mpse+iKO1OGcZ z6<#3#0SG_<0uX=z1Rwwb2tWV=5LiV4zW-1E=^r)-KmY;|fB*y_009U<00Izz00dT8 z0Qdh_xO34o2tWV=5P$##AOHafKmY;|fIu#Q`~L_92tWV=5P$##AOHafKmY;|fWYbt z;Qs&W_c2-s0SG_<0uX=z1Rwwb2tWV=5WxL^!~g^!009U<00Izz00bZa0SG`~^#ySM zfA#wqErb9BAOHafKmY;|fB*y_009W#{y$;>0uX=z1Rwwb2tWV=5P$##Ah7xZxc|TU aeT)`D00Izz00bZa0SG_<0uX=z1pWgATOA7k literal 0 HcmV?d00001 diff --git a/cve_bin_tool/output_engine/__init__.py b/cve_bin_tool/output_engine/__init__.py index 55f7109db7..43d5abfeaf 100644 --- a/cve_bin_tool/output_engine/__init__.py +++ b/cve_bin_tool/output_engine/__init__.py @@ -949,7 +949,6 @@ def generate_sbom( my_package.set_supplier("Organization", product_data.vendor) my_package.set_licensedeclared(license) my_package.set_licenseconcluded(license) - if not ( (my_package.get_name(), my_package.get_value("version")) in sbom_packages @@ -959,9 +958,7 @@ def generate_sbom( my_package.set_evidence(location) # Set location directly sbom_packages[ (my_package.get_name(), my_package.get_value("version")) - ] = (my_package.get_package() - ) - + ] = my_package.get_package() sbom_relationship.initialise() sbom_relationship.set_relationship( root_package, "DEPENDS_ON", product_data.product diff --git a/test/test_html.py b/test/test_html.py index fff10d6c59..7b5c528ad6 100644 --- a/test/test_html.py +++ b/test/test_html.py @@ -312,7 +312,7 @@ def test_cve_remarks_table(self) -> None: def test_empty_cve_list(self) -> None: """Test that the HTML report renders correctly with an empty cve_data["cves"] list.""" empty_output = { - ProductInfo("vendor0", "product0", "1.0"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "usr/local/bin/product"): CVEData( cves=[], paths={""}, ) @@ -328,7 +328,7 @@ def test_empty_cve_list(self) -> None: def test_unknown_cve_number(self) -> None: """Test that the HTML report renders correctly with a cve_data["cves"] list containing an 'UNKNOWN' CVE number.""" unknown_cve_output = { - ProductInfo("vendor0", "product0", "1.0"): CVEData( + ProductInfo("vendor0", "product0", "1.0", "usr/local/bin/product"): CVEData( cves=[ CVE( "UNKNOWN", diff --git a/test/test_sbom.py b/test/test_sbom.py index 3cacdccc97..ffc166b85a 100644 --- a/test/test_sbom.py +++ b/test/test_sbom.py @@ -16,7 +16,10 @@ class TestSBOM: SBOM_PATH = Path(__file__).parent.resolve() / "sbom" PARSED_SBOM_DATA = { ProductInfo( - vendor="gnu", product="glibc", version="2.11.1", location="NotFound" + vendor="gnu", + product="glibc", + version="2.11.1", + location="usr/lib/x86_64-linux-gnu/glibc", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -25,7 +28,10 @@ class TestSBOM: PARSED_SBOM_DATA2 = { ProductInfo( - vendor="ubuntu", product="ubuntu", version="22.04", location="NotFound" + vendor="ubuntu", + product="ubuntu", + version="22.04", + location="usr/local/bin/ubuntu", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -33,21 +39,37 @@ class TestSBOM: } PARSED_SBOM_DATA3 = { ProductInfo( - vendor="gnu", product="glibc", version="2.11.1", location="NotFound" + vendor="gnu", + product="glibc", + version="2.11.1", + location="usr/lib/x86_64-linux-gnu/glibc", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, ProductInfo( - vendor="saxon", product="saxon", version="8.8", location="NotFound" + vendor="saxon", + product="saxon", + version="8.8", + location="usr/local/bin/saxon", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, } SPLIT_DATA = [ - ProductInfo(vendor="openzeppelin", product="contracts", version="4.8.1"), - ProductInfo(vendor="downline_goldmine", product="builder", version="3.2.4"), + ProductInfo( + vendor="openzeppelin", + product="contracts", + version="4.8.1", + location="usr/local/bin/contracts", + ), + ProductInfo( + vendor="downline_goldmine", + product="builder", + version="3.2.4", + location="usr/local/bin/builder", + ), ] @pytest.mark.parametrize( From 001ec726c440e0ac30cd04ecd697b93ded601f58 Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Thu, 4 Apr 2024 11:37:30 +0530 Subject: [PATCH 09/15] test: update --- .coverage.LAPTOP-HA595TQK.23831.XejgJzQx | Bin 53248 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .coverage.LAPTOP-HA595TQK.23831.XejgJzQx diff --git a/.coverage.LAPTOP-HA595TQK.23831.XejgJzQx b/.coverage.LAPTOP-HA595TQK.23831.XejgJzQx deleted file mode 100644 index 63d2dd624e81b097920c3866de42c01dcaf64303..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53248 zcmeI)?{C{g7zc1W+2STm<)Nyws%q-Jfizl@DPj@=UO<7q7)%<23SJb=bpPu>TX{@dE_NZ z48zEmiFntzW02V7eEa!^{f$wt{ASqSZ#=f! z_1`z%S^u-{SASjq!;)2(PGExo1Rwx`|F^*ON!@a`x6NliC9*$KNhDnrWzQR*es!>a zcpwh*BPUo}I zt&KweeiDk27o^P$VlVN+K&W%&P7*b^v4Jf{(;MNLHg_Ceu&SiO%FmLQD_stD`S+sYdJ2g4? zXqIadcqbDz>-?4|a<0r8DoLJXB(CgBzZzNFJ$9 zem0vHUUwi-=ya#?p&u$kH5~tpI`(4kRU({cw_`@yl=1@Z`0psBcz4@m||gs%Dy;hWD3Y z9DB5^^0F0wppS~iv5a{ZiW{OIh9f0|Y;B+%4`aHf7mKtG*H@*FiZbF0F5~F5k=98s zEZ5}}NuL4z`;A$p}uU1)xmXDnTQXG|ts zsxJrGaOX~$EAi}DT`MkIjXsl!N<3c)PLft84XiU63pY}9lGf5@qx`Zk;NI$W`I)~Z zuklB@>8@!x_wJd~Nj?~}d8QZBqV7vw?fh+NAxN&AEaK%cO}O-FXFg8YZjOWBW>?_J zE$hL4rl3>hpLJ?Y{(^7X{~Gjz4FV8=00bZa0SG_<0uX=z1Rwx`)e|V2CDY>X|0Vk` z!~UC|ut5L<5P$##AOHafKmY;|fB*y_@Ma3sOXa(^{u7U`ZI*YO{EqpbW@s8Op* z_6x&)VgK`HHW0Oj00bZa0SG_<0uX=z1Rwwb2tYs!)XR5G{ab)it-Mpse+iKO1OGcZ z6<#3#0SG_<0uX=z1Rwwb2tWV=5LiV4zW-1E=^r)-KmY;|fB*y_009U<00Izz00dT8 z0Qdh_xO34o2tWV=5P$##AOHafKmY;|fIu#Q`~L_92tWV=5P$##AOHafKmY;|fWYbt z;Qs&W_c2-s0SG_<0uX=z1Rwwb2tWV=5WxL^!~g^!009U<00Izz00bZa0SG`~^#ySM zfA#wqErb9BAOHafKmY;|fB*y_009W#{y$;>0uX=z1Rwwb2tWV=5P$##Ah7xZxc|TU aeT)`D00Izz00bZa0SG_<0uX=z1pWgATOA7k From 7154b4ec776c223e62f1691b6e8b00b5f1fee2ac Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Thu, 4 Apr 2024 12:30:35 +0530 Subject: [PATCH 10/15] test: test update for new merges --- cve_bin_tool/sbom_manager/__init__.py | 18 ++++++++++++++++-- cve_bin_tool/version_scanner.py | 2 +- test/test_sbom.py | 12 ++++++------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index c70b83b6fb..640456a8c2 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -79,10 +79,17 @@ def common_prefix_split(self, product, version) -> list[ProductInfo]: len(common_prefix_vendor) == 1 and common_prefix_vendor[0] != "UNKNOWN" ): + location = find_product_location(common_prefix_product) + if location is None: + location = "NotFound" + if validate_location(location) is False: + raise ValueError(f"Invalid location {location} for {product}") found_common_prefix = True for vendor in common_prefix_vendor: parsed_data.append( - ProductInfo(vendor, common_prefix_product, version) + ProductInfo( + vendor, common_prefix_product, version, location + ) ) break if not found_common_prefix: @@ -96,8 +103,15 @@ def common_prefix_split(self, product, version) -> list[ProductInfo]: temp = self.get_vendor(sp) if len(temp) > 1 or (len(temp) == 1 and temp[0] != "UNKNOWN"): for vendor in temp: + location = find_product_location(sp) + if location is None: + location = "NotFound" + if validate_location(location) is False: + raise ValueError( + f"Invalid location {location} for {product}" + ) # if vendor is not None: - parsed_data.append(ProductInfo(vendor, sp, version)) + parsed_data.append(ProductInfo(vendor, sp, version, location)) return parsed_data def scan_file(self) -> dict[ProductInfo, TriageData]: diff --git a/cve_bin_tool/version_scanner.py b/cve_bin_tool/version_scanner.py index be26446624..29b27d56e8 100644 --- a/cve_bin_tool/version_scanner.py +++ b/cve_bin_tool/version_scanner.py @@ -286,7 +286,7 @@ def run_checkers(self, filename: str, lines: str) -> Iterator[ScanInfo]: for vendor, product in checker.VENDOR_PRODUCT: location = find_product_location(product) if location is None: - location = file_path + location = "NotFound" if validate_location(location) is False: raise ValueError( f"Invalid location {location} for {product}" diff --git a/test/test_sbom.py b/test/test_sbom.py index ffc166b85a..aafbdec0f2 100644 --- a/test/test_sbom.py +++ b/test/test_sbom.py @@ -19,7 +19,7 @@ class TestSBOM: vendor="gnu", product="glibc", version="2.11.1", - location="usr/lib/x86_64-linux-gnu/glibc", + location="NotFound", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -31,7 +31,7 @@ class TestSBOM: vendor="ubuntu", product="ubuntu", version="22.04", - location="usr/local/bin/ubuntu", + location="NotFound", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -42,7 +42,7 @@ class TestSBOM: vendor="gnu", product="glibc", version="2.11.1", - location="usr/lib/x86_64-linux-gnu/glibc", + location="NotFound", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -51,7 +51,7 @@ class TestSBOM: vendor="saxon", product="saxon", version="8.8", - location="usr/local/bin/saxon", + location="NotFound", ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -62,13 +62,13 @@ class TestSBOM: vendor="openzeppelin", product="contracts", version="4.8.1", - location="usr/local/bin/contracts", + location="NotFound", ), ProductInfo( vendor="downline_goldmine", product="builder", version="3.2.4", - location="usr/local/bin/builder", + location="NotFound", ), ] From 9c730d59ddeb82805c20b72dad0c23a54586807a Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Thu, 4 Apr 2024 20:36:02 +0530 Subject: [PATCH 11/15] feat: validation update --- cve_bin_tool/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cve_bin_tool/util.py b/cve_bin_tool/util.py index ef64b6c2c4..1ec8519f74 100644 --- a/cve_bin_tool/util.py +++ b/cve_bin_tool/util.py @@ -324,7 +324,7 @@ def validate_location(location: str) -> bool: Validates the location. Returns True if the location is valid, False otherwise. """ - pattern = r"^(?!https?:\/\/)(?=.*\/)(?!.*@)[a-zA-Z0-9_\-\/\s]+|NotFound$" + pattern = r"^(?!https?:\/\/)(?=.*[\\/])(?!.*@)[a-zA-Z0-9_\-\\\/\s]+|NotFound$" return bool(re.match(pattern, location)) From 81c9dd5d35de8032c1c01ed22364541d83aabc5f Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Tue, 16 Apr 2024 21:34:54 +0530 Subject: [PATCH 12/15] test: update --- cve_bin_tool/sbom_manager/__init__.py | 9 ++++--- test/test_util.py | 35 +++++++++++++++++++++------ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index 640456a8c2..34fc1ed410 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -115,6 +115,11 @@ def common_prefix_split(self, product, version) -> list[ProductInfo]: return parsed_data def scan_file(self) -> dict[ProductInfo, TriageData]: + """ + Parses the SBOM input file and returns the product information and corresponding triage data. + Returns: + - dict[ProductInfo, TriageData]: Parsed SBOM data. + """ self.logger.debug( f"Processing SBOM {self.filename} of type {self.type.upper()}" ) @@ -128,12 +133,10 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: modules = self.parse_sbom() except (KeyError, FileNotFoundError, ET.ParseError) as e: LOGGER.debug(e, exc_info=True) - LOGGER.debug( f"The number of modules identified in SBOM - {len(modules)}\n{modules}" ) - # Now process list of modules to create [vendor, product, version] tuples parsed_data: list[ProductInfo] = [] for m in modules: if m and m[0]: @@ -150,7 +153,6 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: raise ValueError( f"Invalid location {location} for {product}" ) - # if vendor is not None: parsed_data.append( ProductInfo(vendor, product, version, location) ) @@ -162,7 +164,6 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: "severity": "", } self.sbom_data[row]["paths"] = set(map(lambda x: x.strip(), "".split(","))) - LOGGER.debug(f"SBOM Data {self.sbom_data}") return self.sbom_data diff --git a/test/test_util.py b/test/test_util.py index 04c7f552ef..822d9b1c70 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -9,6 +9,8 @@ from pathlib import Path from typing import DefaultDict +import pytest + from cve_bin_tool.cve_scanner import CVEScanner from cve_bin_tool.util import CVEData, ProductInfo, find_product_location, inpath @@ -24,20 +26,39 @@ def test_inpath(self): def test_not_inpath(self): assert not inpath("cve_bin_tool_test_for_not_in_path") - def test_find_product_location(self, monkeypatch): - - mock_sys_path = ["/usr/local/bin", "/usr/local/lib/python3.10/site-packages"] + @pytest.mark.parametrize( + "mock_sys_path, known_dirs", + [ + ( + ["/usr/local/bin", "/usr/local/lib/python3.10/site-packages"], + [ + "/usr/local/lib/python3.10/site-packages", + "/usr/local/share", + "/usr/share", + "/usr/local/include", + "/usr/include", + ], + ), + ], + ) + def test_find_product_location(self, monkeypatch, mock_sys_path, known_dirs): product_name = "lib4sbom" monkeypatch.setattr(sys, "path", mock_sys_path) def mock_exists(path): - if "/usr/local/lib/python3.10/site-packages/lib4sbom" in str(path): - return True + for dir in known_dirs: + if dir in str(path): + return True return False - monkeypatch.setattr(Path, "exists", mock_exists) + monkeypatch.setattr("pathlib.Path.exists", mock_exists) - expected_path = "/usr/local/lib/python3.10/site-packages/lib4sbom" + expected_path = None + for dir in known_dirs: + product_location = Path(dir) / product_name + if product_location.exists(): + expected_path = str(product_location) + break assert find_product_location(product_name) == expected_path From 6e07a84aaca9b5cd640e9b6ae16ba0422ca4ce7d Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Tue, 16 Apr 2024 23:04:19 +0530 Subject: [PATCH 13/15] docs: new func docstring --- cve_bin_tool/sbom_manager/__init__.py | 2 +- cve_bin_tool/util.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index 5c349bf1fe..b9b428ef09 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -130,7 +130,7 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: - dict[ProductInfo, TriageData]: Parsed SBOM data. """ - + self.logger.debug( f"Processing SBOM {self.filename} of type {self.type.upper()}" ) diff --git a/cve_bin_tool/util.py b/cve_bin_tool/util.py index 1ec8519f74..d0865a4f44 100644 --- a/cve_bin_tool/util.py +++ b/cve_bin_tool/util.py @@ -287,6 +287,10 @@ def make_http_requests(attribute, **kwargs): def find_product_location(product_name): + """ + Find the location of a product in the system. + Returns the location of the product if found, None otherwise. + """ for path in sys.path: product_location = Path(path) / product_name if product_location.exists(): From 148c5ef22600bbcccc5b6af00be4db3c76579436 Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Wed, 17 Apr 2024 01:57:41 +0530 Subject: [PATCH 14/15] test: update --- test/test_sbom.py | 57 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/test/test_sbom.py b/test/test_sbom.py index f5a1f31607..525e8c252c 100644 --- a/test/test_sbom.py +++ b/test/test_sbom.py @@ -73,22 +73,55 @@ class TestSBOM: ] PARSED_BAD_SBOM_DATA = [ - ProductInfo(vendor="UNKNOWN", product="libjpeg-novendor", version="8b"), - ProductInfo(vendor="libexpat_project", product="libexpat", version="2.0.1"), ProductInfo( - vendor="UNKNOWN", product="ncurses-noversion", version="5.9.noversion" + vendor="UNKNOWN", + product="libjpeg-novendor", + version="8b", + location="NotFound", + ), + ProductInfo( + vendor="libexpat_project", + product="libexpat", + version="2.0.1", + location="NotFound", + ), + ProductInfo( + vendor="UNKNOWN", + product="ncurses-noversion", + version="5.9.noversion", + location="NotFound", + ), + ProductInfo( + vendor="zlib", product="zlib", version="1.2.3", location="NotFound" ), - ProductInfo(vendor="zlib", product="zlib", version="1.2.3"), ] PARSED_EXT_REF_PRIORITY_SBOM_DATA = [ - ProductInfo(vendor="ijg", product="libjpeg", version="8b"), - ProductInfo(vendor="libexpat_project", product="libexpat", version="2.0.1"), - ProductInfo(vendor="gnu", product="ncurses", version="5.9"), - ProductInfo(vendor="unknown", product="ncurses", version="5.9"), - ProductInfo(vendor="ncurses_project", product="ncurses", version="5.9"), - ProductInfo(vendor="zlib", product="zlib", version="1.2.3"), - ProductInfo(vendor="unknown", product="zlib", version="1.2.3"), - ProductInfo(vendor="gnu", product="zlib", version="1.2.3"), + ProductInfo(vendor="ijg", product="libjpeg", version="8b", location="NotFound"), + ProductInfo( + vendor="libexpat_project", + product="libexpat", + version="2.0.1", + location="NotFound", + ), + ProductInfo( + vendor="gnu", product="ncurses", version="5.9", location="NotFound" + ), + ProductInfo( + vendor="unknown", product="ncurses", version="5.9", location="NotFound" + ), + ProductInfo( + vendor="ncurses_project", + product="ncurses", + version="5.9", + location="NotFound", + ), + ProductInfo( + vendor="zlib", product="zlib", version="1.2.3", location="NotFound" + ), + ProductInfo( + vendor="unknown", product="zlib", version="1.2.3", location="NotFound" + ), + ProductInfo(vendor="gnu", product="zlib", version="1.2.3", location="NotFound"), ] @pytest.mark.parametrize( From 793340d67763ba9b24aa272a1de0d07f9f02ccc6 Mon Sep 17 00:00:00 2001 From: Mayank Rai Date: Wed, 17 Apr 2024 04:44:30 +0530 Subject: [PATCH 15/15] test: update --- cve_bin_tool/sbom_manager/__init__.py | 46 ++++++++++++++++----------- test/test_sbom.py | 20 +++--------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/cve_bin_tool/sbom_manager/__init__.py b/cve_bin_tool/sbom_manager/__init__.py index b9b428ef09..ca5935679a 100644 --- a/cve_bin_tool/sbom_manager/__init__.py +++ b/cve_bin_tool/sbom_manager/__init__.py @@ -130,7 +130,6 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: - dict[ProductInfo, TriageData]: Parsed SBOM data. """ - self.logger.debug( f"Processing SBOM {self.filename} of type {self.type.upper()}" ) @@ -144,29 +143,37 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: modules = self.parse_sbom() except (KeyError, FileNotFoundError, ET.ParseError) as e: LOGGER.debug(e, exc_info=True) + LOGGER.debug( f"The number of modules identified in SBOM - {len(modules)}\n{modules}" ) + # Now process list of modules to create [vendor, product, version] tuples parsed_data: list[ProductInfo] = [] - for m in modules: - if m and m[0]: - # Using lower to normalize product names across databases - product, version = m[0].lower(), m[1] - if version != "": - # Now add vendor to create product record.... - vendor_set = self.get_vendor(product) - for vendor in vendor_set: - location = find_product_location(product) - if location is None: - location = "NotFound" - if validate_location(location) is False: - raise ValueError( - f"Invalid location {location} for {product}" - ) - parsed_data.append( - ProductInfo(vendor, product, version, location) - ) + for module_vendor, product, version in modules: + # Using lower to normalize product names across databases + product = product.lower() + + if module_vendor is None: + # Now add vendor to create product record.... + vendor_set = self.get_vendor(product) + for vendor in vendor_set: + # if vendor is not None: + location = find_product_location(product) + if location is None: + location = "NotFound" + if validate_location(location) is False: + raise ValueError(f"Invalid location {location} for {product}") + parsed_data.append(ProductInfo(vendor, product, version, location)) + else: + location = find_product_location(product) + if location is None: + location = "NotFound" + if validate_location(location) is False: + raise ValueError(f"Invalid location {location} for {product}") + parsed_data.append( + ProductInfo(module_vendor, product, version, location) + ) for row in parsed_data: self.sbom_data[row]["default"] = { @@ -175,6 +182,7 @@ def scan_file(self) -> dict[ProductInfo, TriageData]: "severity": "", } self.sbom_data[row]["paths"] = set(map(lambda x: x.strip(), "".split(","))) + LOGGER.debug(f"SBOM Data {self.sbom_data}") return self.sbom_data diff --git a/test/test_sbom.py b/test/test_sbom.py index 525e8c252c..134eafd327 100644 --- a/test/test_sbom.py +++ b/test/test_sbom.py @@ -16,10 +16,7 @@ class TestSBOM: SBOM_PATH = Path(__file__).parent.resolve() / "sbom" PARSED_SBOM_DATA = { ProductInfo( - vendor="gnu", - product="glibc", - version="2.11.1", - location="NotFound", + vendor="gnu", product="glibc", version="2.11.1", location="NotFound" ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -28,10 +25,7 @@ class TestSBOM: PARSED_SBOM_DATA2 = { ProductInfo( - vendor="ubuntu", - product="ubuntu", - version="22.04", - location="NotFound", + vendor="ubuntu", product="ubuntu", version="22.04", location="NotFound" ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, @@ -39,19 +33,13 @@ class TestSBOM: } PARSED_SBOM_DATA3 = { ProductInfo( - vendor="gnu", - product="glibc", - version="2.11.1", - location="NotFound", + vendor="gnu", product="glibc", version="2.11.1", location="NotFound" ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""}, }, ProductInfo( - vendor="saxon", - product="saxon", - version="8.8", - location="NotFound", + vendor="saxon", product="saxon", version="8.8", location="NotFound" ): { "default": {"remarks": Remarks.NewFound, "comments": "", "severity": ""}, "paths": {""},