From 8f269cd8b86772f18804e84873554ef4546281ce Mon Sep 17 00:00:00 2001 From: joydeep049 Date: Fri, 13 Jun 2025 17:35:10 +0530 Subject: [PATCH 1/4] feat: No Scan SOC for language parsers Signed-off-by: joydeep049 --- cve_bin_tool/cli.py | 198 +++++++++++++++-------------- cve_bin_tool/parsers/ccpp.py | 29 ++++- cve_bin_tool/parsers/dart.py | 16 ++- cve_bin_tool/parsers/env.py | 14 +- cve_bin_tool/parsers/go.py | 15 ++- cve_bin_tool/parsers/javascript.py | 48 +++++-- cve_bin_tool/parsers/perl.py | 15 ++- cve_bin_tool/parsers/php.py | 15 ++- cve_bin_tool/parsers/python.py | 31 ++++- cve_bin_tool/parsers/r.py | 15 ++- cve_bin_tool/parsers/ruby.py | 15 ++- cve_bin_tool/parsers/rust.py | 15 ++- cve_bin_tool/parsers/swift.py | 15 ++- cve_bin_tool/version_scanner.py | 11 +- 14 files changed, 300 insertions(+), 152 deletions(-) diff --git a/cve_bin_tool/cli.py b/cve_bin_tool/cli.py index f7f520dc9c..9b66bcf07f 100644 --- a/cve_bin_tool/cli.py +++ b/cve_bin_tool/cli.py @@ -822,112 +822,113 @@ def main(argv=None): ) enabled_sources = [source_nvd] + enabled_sources - # Database update related settings - # Connect to the database - cvedb_orig = CVEDB( - sources=enabled_sources, - version_check=not version_check, - error_mode=error_mode, - ) - - # if OLD_CACHE_DIR (from cvedb.py) exists, print warning - if Path(OLD_CACHE_DIR).exists(): - LOGGER.warning( - f"Obsolete cache dir {OLD_CACHE_DIR} is no longer needed and can be removed." + if not args["no_scan"]: + # Database update related settings + # Connect to the database + cvedb_orig = CVEDB( + sources=enabled_sources, + version_check=not version_check, + error_mode=error_mode, ) - # Check database exists if operating in offline mode. - if args["offline"] and not cvedb_orig.check_db_exists(): - LOGGER.critical("Database does not exist.") - LOGGER.info( - "Consult the documentation at https://cve-bin-tool.readthedocs.io/en/latest/how_to_guides/offline.html to find out how to setup offline operation." - ) - return ERROR_CODES[CVEDBNotExist] + # if OLD_CACHE_DIR (from cvedb.py) exists, print warning + if Path(OLD_CACHE_DIR).exists(): + LOGGER.warning( + f"Obsolete cache dir {OLD_CACHE_DIR} is no longer needed and can be removed." + ) - if args["use_mirror"] and not args["offline"]: - if ( - cvedb_orig.fetch_from_mirror( - mirror=args["use_mirror"], + # Check database exists if operating in offline mode. + if args["offline"] and not cvedb_orig.check_db_exists(): + LOGGER.critical("Database does not exist.") + LOGGER.info( + "Consult the documentation at https://cve-bin-tool.readthedocs.io/en/latest/how_to_guides/offline.html to find out how to setup offline operation." + ) + return ERROR_CODES[CVEDBNotExist] + + if args["use_mirror"] and not args["offline"]: + if ( + cvedb_orig.fetch_from_mirror( + mirror=args["use_mirror"], + pubkey=args["verify"], + ignore_signature=args["ignore_sig"], + log_signature_error=args["log_signature_error"], + ) + == -1 + ): + return ERROR_CODES[MirrorError] + + # import database from JSON chopped by years + if args["import_json"] and cvedb_orig.check_db_exists(): + return_code = cvedb_orig.json_to_db_wrapper( + path=args["import_json"], pubkey=args["verify"], ignore_signature=args["ignore_sig"], log_signature_error=args["log_signature_error"], ) - == -1 - ): - return ERROR_CODES[MirrorError] - - # import database from JSON chopped by years - if args["import_json"] and cvedb_orig.check_db_exists(): - return_code = cvedb_orig.json_to_db_wrapper( - path=args["import_json"], - pubkey=args["verify"], - ignore_signature=args["ignore_sig"], - log_signature_error=args["log_signature_error"], - ) - # And terminate operation - return return_code - - # Export database as JSON chopped by years - if args["export_json"] and cvedb_orig.check_db_exists(): - return_code = cvedb_orig.db_to_json( - path=args["export_json"], - private_key=args["pgp_sign"], - passphrase=args["passphrase"], - ) - # And terminate operation - return return_code - - # Import database if file exists - if args["import"] and Path(args["import"]).exists(): - LOGGER.info(f'Import database from {args["import"]}') - cvedb_orig.copy_db(filename=args["import"], export=False) - - # Export database if database exists - if args["export"] and cvedb_orig.check_db_exists(): - LOGGER.info(f'Export database to {args["export"]}') - cvedb_orig.copy_db(filename=args["export"], export=True) - # And terminate operation - return 0 - - # Clear data if -u now is set - if db_update == "now": - cvedb_orig.clear_cached_data() + # And terminate operation + return return_code + + # Export database as JSON chopped by years + if args["export_json"] and cvedb_orig.check_db_exists(): + return_code = cvedb_orig.db_to_json( + path=args["export_json"], + private_key=args["pgp_sign"], + passphrase=args["passphrase"], + ) + # And terminate operation + return return_code + + # Import database if file exists + if args["import"] and Path(args["import"]).exists(): + LOGGER.info(f'Import database from {args["import"]}') + cvedb_orig.copy_db(filename=args["import"], export=False) + + # Export database if database exists + if args["export"] and cvedb_orig.check_db_exists(): + LOGGER.info(f'Export database to {args["export"]}') + cvedb_orig.copy_db(filename=args["export"], export=True) + # And terminate operation + return 0 - if db_update == "latest": - cvedb_orig.refresh_cache_and_update_db() + # Clear data if -u now is set + if db_update == "now": + cvedb_orig.clear_cached_data() + + if db_update == "latest": + cvedb_orig.refresh_cache_and_update_db() + + # update db if needed + if db_update != "never" and not args["no_scan"]: + cvedb_orig.get_cvelist_if_stale() + elif not args["no"]: + LOGGER.warning("Not verifying CVE DB cache") + if not cvedb_orig.check_cve_entries(): + with ErrorHandler(mode=error_mode, logger=LOGGER): + raise EmptyCache(cvedb_orig.cachedir) + if not cvedb_orig.latest_schema(): + LOGGER.critical("Database does not have the latest schema.") + LOGGER.info("Please update database, by using --update 'now'") + if args["offline"]: + LOGGER.info( + "Consult the documentation at https://cve-bin-tool.readthedocs.io/en/latest/how_to_guides/offline.html to find out how to setup offline operation." + ) + return ERROR_CODES[CVEDBOutdatedSchema] - # update db if needed - if db_update != "never": - cvedb_orig.get_cvelist_if_stale() - else: - LOGGER.warning("Not verifying CVE DB cache") + # CVE Database validation if not cvedb_orig.check_cve_entries(): with ErrorHandler(mode=error_mode, logger=LOGGER): - raise EmptyCache(cvedb_orig.cachedir) - if not cvedb_orig.latest_schema(): - LOGGER.critical("Database does not have the latest schema.") - LOGGER.info("Please update database, by using --update 'now'") - if args["offline"]: - LOGGER.info( - "Consult the documentation at https://cve-bin-tool.readthedocs.io/en/latest/how_to_guides/offline.html to find out how to setup offline operation." - ) - return ERROR_CODES[CVEDBOutdatedSchema] + raise CVEDataMissing("No data in CVE Database") - # CVE Database validation - if not cvedb_orig.check_cve_entries(): - with ErrorHandler(mode=error_mode, logger=LOGGER): - raise CVEDataMissing("No data in CVE Database") - - # Report time of last database update - db_date = time.strftime( - "%d %B %Y at %H:%M:%S", time.localtime(cvedb_orig.get_db_update_date()) - ) - LOGGER.info( - "CVE database contains CVEs from National Vulnerability Database (NVD), Open Source Vulnerability Database (OSV), Gitlab Advisory Database (GAD) and RedHat" - ) - LOGGER.info(f"CVE database last updated on {db_date}") + # Report time of last database update + db_date = time.strftime( + "%d %B %Y at %H:%M:%S", time.localtime(cvedb_orig.get_db_update_date()) + ) + LOGGER.info( + "CVE database contains CVEs from National Vulnerability Database (NVD), Open Source Vulnerability Database (OSV), Gitlab Advisory Database (GAD) and RedHat" + ) + LOGGER.info(f"CVE database last updated on {db_date}") - cvedb_orig.remove_cache_backup() + cvedb_orig.remove_cache_backup() output_formats = set(args["format"].split(",")) output_formats = [output_format.strip() for output_format in output_formats] @@ -1083,14 +1084,18 @@ def main(argv=None): # Root package for generated SBOM. Will be updated to reflect input data sbom_root = "CVE-SCAN" + if args["no_scan"]: + cvedb_orig = None + disabled_sources = None + with CVEScanner( score=score, check_metrics=metrics, epss_percentile=epss_percentile, epss_probability=epss_probability, check_exploits=args["exploits"], - exploits_list=cvedb_orig.get_exploits_list(), - disabled_sources=disabled_sources, + exploits_list=cvedb_orig.get_exploits_list() if cvedb_orig else [], + disabled_sources=disabled_sources or [], ) as cve_scanner: triage_data: TriageData total_files: int = 0 @@ -1157,7 +1162,8 @@ def main(argv=None): LOGGER.debug(f"Triage Data: {triage_data}") parsed_data[product_info] = triage_data - cve_scanner.get_cves(product_info, triage_data) + if not args["no_scan"]: + cve_scanner.get_cves(product_info, triage_data) total_files = version_scanner.total_scanned_files LOGGER.info(f"Total files: {total_files}") diff --git a/cve_bin_tool/parsers/ccpp.py b/cve_bin_tool/parsers/ccpp.py index 8d4452e940..2cc151d430 100644 --- a/cve_bin_tool/parsers/ccpp.py +++ b/cve_bin_tool/parsers/ccpp.py @@ -6,6 +6,7 @@ import re from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class CCppParser(Parser): @@ -51,15 +52,31 @@ def run_checker(self, filename): product = require.split("#")[0].split("/")[0] version = require.split("#")[0].split("/")[1] purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) - if vendor is not None: - yield from vendor + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + if vendor is not None: + yield from vendor if build_requires: for build_require in build_requires: product = build_require.split("#")[0].split("/")[0] version = build_require.split("#")[0].split("/")[1] purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) - if vendor is not None: - yield from vendor + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + if vendor is not None: + yield from vendor self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/parsers/dart.py b/cve_bin_tool/parsers/dart.py index 91f79075a7..29aee9a997 100644 --- a/cve_bin_tool/parsers/dart.py +++ b/cve_bin_tool/parsers/dart.py @@ -6,6 +6,7 @@ import yaml from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class DartParser(Parser): @@ -54,7 +55,16 @@ def run_checker(self, filename): product = package_name version = package_detail.get("version").replace('"', "") purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) - if vendor: - yield from vendor + + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + if vendor: + yield from vendor self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/parsers/env.py b/cve_bin_tool/parsers/env.py index 5612b23613..5c776ca861 100644 --- a/cve_bin_tool/parsers/env.py +++ b/cve_bin_tool/parsers/env.py @@ -45,6 +45,11 @@ class EnvParser(Parser): requirements.txt) and generate PURLs (Package URLs) for the listed packages. """ + def __init__(self, cve_db, logger): + super().__init__(cve_db, logger) + self.cve_db = cve_db + self.logger = logger + PARSER_MATCH_FILENAMES = [ ".env", ] @@ -127,10 +132,11 @@ def run_checker(self, filename): for _namespace, cve in env_config.namespaces.items() ] - with self.cve_db.with_cursor() as cursor: - self.cve_db.populate_cve_metrics(severity_data, cursor) - self.cve_db.populate_severity(severity_data, cursor, data_source) - self.cve_db.populate_affected(affected_data, cursor, data_source) + if self.cve_db: + with self.cve_db.with_cursor() as cursor: + self.cve_db.populate_cve_metrics(severity_data, cursor) + self.cve_db.populate_severity(severity_data, cursor, data_source) + self.cve_db.populate_affected(affected_data, cursor, data_source) for _namespace, cve in env_config.namespaces.items(): yield ScanInfo( diff --git a/cve_bin_tool/parsers/go.py b/cve_bin_tool/parsers/go.py index e01727f3a0..ab11d29dee 100644 --- a/cve_bin_tool/parsers/go.py +++ b/cve_bin_tool/parsers/go.py @@ -4,6 +4,7 @@ import re from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class GoParser(Parser): @@ -75,7 +76,15 @@ def run_checker(self, filename): product = line.split(" ")[0].split("/")[-1] version = line.split(" ")[1][1:].split("-")[0].split("+")[0] purl = self.generate_purl(product) - vendors = self.get_vendor(purl, product, version) - if vendors is not None: - yield from vendors + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendors = self.get_vendor(purl, product, version) + if vendors is not None: + yield from vendors self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/parsers/javascript.py b/cve_bin_tool/parsers/javascript.py index d12ca2dca6..13cf2e57ca 100644 --- a/cve_bin_tool/parsers/javascript.py +++ b/cve_bin_tool/parsers/javascript.py @@ -6,6 +6,7 @@ import re from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class JavascriptParser(Parser): @@ -58,7 +59,16 @@ def process_package_lock(self, filename): product = data["name"] version = data["version"] purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) + if not self.cve_db: + vendor = None + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) else: vendor = None if vendor is not None: @@ -108,9 +118,17 @@ def process_package_lock(self, filename): for product, version in product_version_mapping: purl = self.generate_purl(product, "") - vendor = self.get_vendor(purl, product, version) - if vendor is not None: - yield from vendor + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + if vendor is not None: + yield from vendor def process_yarn_lock(self, filename): """Process yarn.lock file and extract product and dependency details @@ -136,11 +154,19 @@ def process_yarn_lock(self, filename): for product, version in product_version_mapping: # Generate the PURL for the package purl = self.generate_purl(product, "", version) - vendor, result = self.find_vendor_from_purl(purl, version) + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor, result = self.find_vendor_from_purl(purl, version) - if not result: - # If no vendor found using PURL, try to find vendor using product and version - vendor = self.find_vendor(product, version) - if vendor is not None: - # Yield vendor information if found - yield from vendor + if not result: + # If no vendor found using PURL, try to find vendor using product and version + vendor = self.find_vendor(product, version) + if vendor is not None: + # Yield vendor information if found + yield from vendor diff --git a/cve_bin_tool/parsers/perl.py b/cve_bin_tool/parsers/perl.py index b018871619..9e90343f9c 100644 --- a/cve_bin_tool/parsers/perl.py +++ b/cve_bin_tool/parsers/perl.py @@ -4,6 +4,7 @@ import re from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class PerlParser(Parser): @@ -59,7 +60,15 @@ def run_checker(self, filename): product = dependency[0] version = dependency[1] purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) - if vendor is not None: - yield from vendor + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + if vendor is not None: + yield from vendor self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/parsers/php.py b/cve_bin_tool/parsers/php.py index 37a4d4acb6..18007ae801 100644 --- a/cve_bin_tool/parsers/php.py +++ b/cve_bin_tool/parsers/php.py @@ -8,6 +8,7 @@ import re from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class PhpParser(Parser): @@ -58,7 +59,15 @@ def run_checker(self, filename): if "dev" in version: continue purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) - if vendor is not None: - yield from vendor + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + if vendor is not None: + yield from vendor self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/parsers/python.py b/cve_bin_tool/parsers/python.py index 827d717224..3c439fa94d 100644 --- a/cve_bin_tool/parsers/python.py +++ b/cve_bin_tool/parsers/python.py @@ -10,6 +10,7 @@ from cve_bin_tool.parsers import Parser from cve_bin_tool.strings import parse_strings +from cve_bin_tool.util import ProductInfo, ScanInfo class PythonRequirementsParser(Parser): @@ -100,10 +101,18 @@ def run_checker(self, filename): product = line["metadata"]["name"] version = line["metadata"]["version"] purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) - - if vendor is not None: - yield from vendor + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + + if vendor is not None: + yield from vendor self.logger.debug(f"Done scanning file: {self.filename}") @@ -153,9 +162,17 @@ def run_checker(self, filename): product = search(compile(r"^Name: (.+)$", MULTILINE), lines).group(1) version = search(compile(r"^Version: (.+)$", MULTILINE), lines).group(1) purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) - if vendor is not None: - yield from vendor + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + if vendor is not None: + yield from vendor # 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/parsers/r.py b/cve_bin_tool/parsers/r.py index 4b08f3393a..40a5c27e5a 100644 --- a/cve_bin_tool/parsers/r.py +++ b/cve_bin_tool/parsers/r.py @@ -5,6 +5,7 @@ import re from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class RParser(Parser): @@ -61,7 +62,15 @@ def run_checker(self, filename): product = content["Packages"][package]["Package"] version = content["Packages"][package]["Version"] purl = self.generate_purl(product) - vendor = self.get_vendor(purl, product, version) - if vendor is not None: - yield from vendor + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendor = self.get_vendor(purl, product, version) + if vendor is not None: + yield from vendor self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/parsers/ruby.py b/cve_bin_tool/parsers/ruby.py index 0efa8c4046..1937e8e33f 100644 --- a/cve_bin_tool/parsers/ruby.py +++ b/cve_bin_tool/parsers/ruby.py @@ -6,6 +6,7 @@ import re from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class RubyParser(Parser): @@ -73,7 +74,15 @@ def run_checker(self, filename): product = line.strip().split()[0] version = line.strip().split("(")[1][:-1] purl = self.generate_purl(product) - vendors = self.get_vendor(purl, product, version) - if vendors is not None: - yield from vendors + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendors = self.get_vendor(purl, product, version) + if vendors is not None: + yield from vendors self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/parsers/rust.py b/cve_bin_tool/parsers/rust.py index 20f8b155b3..34c633aa75 100644 --- a/cve_bin_tool/parsers/rust.py +++ b/cve_bin_tool/parsers/rust.py @@ -4,6 +4,7 @@ import re from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class RustParser(Parser): @@ -66,9 +67,17 @@ def run_checker(self, filename): continue purl = self.generate_purl(product) - vendors = self.get_vendor(purl, product, version) - if vendors is not None: - yield from vendors + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendors = self.get_vendor(purl, product, version) + if vendors is not None: + yield from vendors product = "" version = "" self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/parsers/swift.py b/cve_bin_tool/parsers/swift.py index bbe6d6b2e9..dbd177fb0b 100644 --- a/cve_bin_tool/parsers/swift.py +++ b/cve_bin_tool/parsers/swift.py @@ -7,6 +7,7 @@ from urllib.parse import urlparse from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo class SwiftParser(Parser): @@ -74,7 +75,15 @@ def run_checker(self, filename): self.logger.debug(domain) purl = self.generate_purl(product) - vendors = self.get_vendor(purl, product, version) - if vendors is not None: - yield from vendors + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + " ", + ), + ] + else: + vendors = self.get_vendor(purl, product, version) + if vendors is not None: + yield from vendors self.logger.debug(f"Done scanning file: {self.filename}") diff --git a/cve_bin_tool/version_scanner.py b/cve_bin_tool/version_scanner.py index 7940e51f96..f020dcc5c7 100644 --- a/cve_bin_tool/version_scanner.py +++ b/cve_bin_tool/version_scanner.py @@ -68,12 +68,16 @@ def __init__( self.should_extract = should_extract self.file_stack: list[str] = [] self.error_mode = error_mode - self.cve_db = CVEDB(sources=sources) self.validate = validate self.logger.info("Checkers loaded: %s" % (", ".join(self.checkers.keys()))) self.language_checkers = valid_files self.language_checkers_names = self.available_language_checkers() + if self.no_scan: + self.cve_db = None + else: + self.cve_db = CVEDB(sources=sources) + @classmethod def load_checkers(cls) -> dict[str, type[Checker]]: """Loads CVE checkers""" @@ -262,9 +266,8 @@ def scan_file(self, filename: str) -> Iterator[ScanInfo]: # parse binary file's strings lines = parse_strings(filename) - if self.no_scan: - yield from self.run_checkers(filename, lines) - return + if not self.no_scan and not self.cve_db: + self.logger.info("No Database Object Found: Fallback to No-Scan Mode") if output: valid_file = False From 1f1ea97b9d824127d5622ffc0d7afae19096a9da Mon Sep 17 00:00:00 2001 From: joydeep049 Date: Fri, 13 Jun 2025 18:13:42 +0530 Subject: [PATCH 2/4] fix: failing tests Signed-off-by: joydeep049 --- cve_bin_tool/cli.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cve_bin_tool/cli.py b/cve_bin_tool/cli.py index 9b66bcf07f..51e4bf6d8d 100644 --- a/cve_bin_tool/cli.py +++ b/cve_bin_tool/cli.py @@ -898,9 +898,8 @@ def main(argv=None): cvedb_orig.refresh_cache_and_update_db() # update db if needed - if db_update != "never" and not args["no_scan"]: + if db_update != "never": cvedb_orig.get_cvelist_if_stale() - elif not args["no"]: LOGGER.warning("Not verifying CVE DB cache") if not cvedb_orig.check_cve_entries(): with ErrorHandler(mode=error_mode, logger=LOGGER): From 59cc249209112ef5cdfa2d84b133afdc41b88db7 Mon Sep 17 00:00:00 2001 From: joydeep049 Date: Mon, 16 Jun 2025 13:30:22 +0530 Subject: [PATCH 3/4] fix: resolve failing tests Signed-off-by: joydeep049 --- cve_bin_tool/cli.py | 1 + test/test_javascript.py | 26 +++++++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/cve_bin_tool/cli.py b/cve_bin_tool/cli.py index 51e4bf6d8d..3daca24d91 100644 --- a/cve_bin_tool/cli.py +++ b/cve_bin_tool/cli.py @@ -900,6 +900,7 @@ def main(argv=None): # update db if needed if db_update != "never": cvedb_orig.get_cvelist_if_stale() + else: LOGGER.warning("Not verifying CVE DB cache") if not cvedb_orig.check_cve_entries(): with ErrorHandler(mode=error_mode, logger=LOGGER): diff --git a/test/test_javascript.py b/test/test_javascript.py index f0b353a364..125a3a98f6 100644 --- a/test/test_javascript.py +++ b/test/test_javascript.py @@ -4,6 +4,7 @@ import json from cve_bin_tool.parsers.javascript import JavascriptParser +from cve_bin_tool.util import ProductInfo, ScanInfo # Dummy logger to suppress debug output @@ -18,13 +19,21 @@ def __init__(self): super().__init__(cve_db=None, logger=DummyLogger()) def get_vendor(self, purl, product, version): - return ["vendor_dummy"] + return [ScanInfo(ProductInfo("UNKNOWN", product, version, purl), " ")] def find_vendor_from_purl(self, purl, version): - return (["vendor_yarn"], True) + return ([ScanInfo(ProductInfo("UNKNOWN", purl.name, version, purl), " ")], True) def find_vendor(self, product, version): - return ["vendor_yarn_alt"] + return [ScanInfo(ProductInfo("UNKNOWN", product, version), " ")] + + def run_checker(self, filename): + # Override run_checker to bypass no_scan mode check + if filename.endswith("package-lock.json"): + return self.process_package_lock(filename) + elif filename.endswith("yarn.lock"): + return self.process_yarn_lock(filename) + return [] # Test for package-lock.json branch with lockfileVersion>=2 @@ -46,7 +55,11 @@ def test_process_package_lock_lockfile_v2(tmp_path): # - package "" -> yields vendor_dummy # - its require "dep1" yields vendor_dummy (skip if version=="*") # - package "node_modules/dep3" yields vendor_dummy - expected = ["vendor_dummy", "vendor_dummy", "vendor_dummy"] + expected = [ + ScanInfo(ProductInfo("UNKNOWN", "", "1.0.0", None), " "), + ScanInfo(ProductInfo("UNKNOWN", "dep1", "1.2.3", None), " "), + ScanInfo(ProductInfo("UNKNOWN", "dep3", "2.3.4", None), " "), + ] assert results == expected @@ -63,5 +76,8 @@ def test_process_yarn_lock(tmp_path): parser = DummyJavascriptParser() results = list(parser.run_checker(str(file_path))) # Two matches yield vendor from find_vendor_from_purl for each - expected = ["vendor_yarn", "vendor_yarn"] + expected = [ + ScanInfo(ProductInfo("UNKNOWN", "somepackage", "1.0.0", None), " "), + ScanInfo(ProductInfo("UNKNOWN", "anotherpkg", "2.0.0", None), " "), + ] assert results == expected From 62e75e3e0ed8e9b4b6056e6e87ff5a774f54bb41 Mon Sep 17 00:00:00 2001 From: joydeep049 Date: Mon, 30 Jun 2025 20:24:26 +0530 Subject: [PATCH 4/4] feat: added no-scan SOC for JavaParser Signed-off-by: joydeep049 --- cve_bin_tool/parsers/java.py | 48 +++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/cve_bin_tool/parsers/java.py b/cve_bin_tool/parsers/java.py index ac84836931..48a515beb5 100644 --- a/cve_bin_tool/parsers/java.py +++ b/cve_bin_tool/parsers/java.py @@ -99,12 +99,20 @@ def run_checker(self, filename): product = parent.find(schema + "artifactId").text if product is not None and version is not None: purl = self.generate_purl(product) - product_info, result = self.find_vendor_from_purl(purl, version) - if not result: - product_info = self.find_vendor(product, version) - product_info = self.mismatch(purl, product_info) - if product_info is not None: - yield from product_info + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo("UNKNOWN", product, version, purl), + file_path, + ), + ] + else: + product_info, result = self.find_vendor_from_purl(purl, version) + if not result: + product_info = self.find_vendor(product, version) + product_info = self.mismatch(purl, product_info) + if product_info is not None: + yield from product_info # Some version strings are defined as properties. # Build up dictionary of values in same format ${name} : {value} @@ -136,14 +144,24 @@ def run_checker(self, filename): if version[0].isdigit(): # Valid version identifier purl = self.generate_purl(product.text) - product_info, result = self.find_vendor_from_purl( - purl, version - ) - if not result: - product_info = self.find_vendor( - product.text, version + if not self.cve_db: + yield from [ + ScanInfo( + ProductInfo( + "UNKNOWN", product.text, version, purl + ), + file_path, + ), + ] + else: + product_info, result = self.find_vendor_from_purl( + purl, version ) - product_info = self.mismatch(purl, product_info) - if product_info is not None: - yield from product_info + if not result: + product_info = self.find_vendor( + product.text, version + ) + product_info = self.mismatch(purl, product_info) + if product_info is not None: + yield from product_info self.logger.debug(f"Done scanning file: {filename}")