diff --git a/cve_bin_tool/parsers/__init__.py b/cve_bin_tool/parsers/__init__.py index 29782163ab..8307b80ac0 100644 --- a/cve_bin_tool/parsers/__init__.py +++ b/cve_bin_tool/parsers/__init__.py @@ -1,9 +1,7 @@ # Copyright (C) 2022 Intel Corporation # SPDX-License-Identifier: GPL-3.0-or-later -__all__ = [ - "Parser", -] +__all__ = ["Parser", "javascript"] class Parser: diff --git a/cve_bin_tool/parsers/javascript.py b/cve_bin_tool/parsers/javascript.py new file mode 100644 index 0000000000..ccecf307ca --- /dev/null +++ b/cve_bin_tool/parsers/javascript.py @@ -0,0 +1,73 @@ +# Copyright (C) 2022 Intel Corporation +# SPDX-License-Identifier: GPL-3.0-or-later + +import json + +from cve_bin_tool.parsers import Parser +from cve_bin_tool.util import ProductInfo, ScanInfo + + +class JavascriptParser(Parser): + def __init__(self, cve_db, logger): + self.cve_db = cve_db + self.logger = logger + self.filename = "" + + def find_vendor(self, product, version): + """Find vendor for Javascript product""" + if version == "*": + return None + vendor_package_pair = self.cve_db.get_vendor_product_pairs(product) + vendorlist: list[ScanInfo] = [] + if vendor_package_pair != []: + # To handle multiple vendors, return all combinations of product/vendor mappings + for v in vendor_package_pair: + vendor = v["vendor"] + file_path = self.filename + # Tidy up version string + if "^" in version: + version = version[1:] + self.logger.debug(f"{file_path} {product} {version} by {vendor}") + vendorlist.append( + ScanInfo(ProductInfo(vendor, product, version), file_path) + ) + return vendorlist if len(vendorlist) > 0 else None + return None + + def run_checker(self, filename): + """Process package-lock.json file and extract product and dependency details""" + self.filename = filename + with open(self.filename) as fh: + data = json.load(fh) + product = data["name"] + version = data["version"] + vendor = self.find_vendor(product, version) + if vendor is not None: + yield from vendor + # Now process dependencies + for i in data["dependencies"]: + # To handle @actions/: lines, extract product name from line + product = i.split("/")[1] if "/" in i else i + # Handle different formats. Either : or + # : { + # ... + # "version" : + # ... + # } + try: + version = data["dependencies"][i]["version"] + except Exception: + # Cater for case when version field not present + version = data["dependencies"][i] + vendor = self.find_vendor(product, version) + if vendor is not None: + yield from vendor + if "requires" in data["dependencies"][i]: + for r in data["dependencies"][i]["requires"]: + # To handle @actions/: lines, extract product name from line + product = r.split("/")[1] if "/" in r else r + version = data["dependencies"][i]["requires"][r] + vendor = self.find_vendor(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/version_scanner.py b/cve_bin_tool/version_scanner.py index 6c3d550263..89aafe5c60 100644 --- a/cve_bin_tool/version_scanner.py +++ b/cve_bin_tool/version_scanner.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import annotations -import json import os import subprocess import sys @@ -19,6 +18,7 @@ from cve_bin_tool.extractor import Extractor, TempDirExtractorContext from cve_bin_tool.file import is_binary from cve_bin_tool.log import LOGGER +from cve_bin_tool.parsers.javascript import JavascriptParser from cve_bin_tool.strings import Strings from cve_bin_tool.util import DirWalk, ProductInfo, ScanInfo, inpath from cve_bin_tool.validator import validate_pom @@ -272,63 +272,9 @@ def run_java_checker(self, filename: str) -> Iterator[ScanInfo]: self.logger.debug(f"Done scanning file: {filename}") - def find_js_vendor(self, product: str, version: str) -> list[ScanInfo] | None: - """Find vendor for Javascript product""" - if version == "*": - return None - vendor_package_pair = self.cve_db.get_vendor_product_pairs(product) - vendorlist: list[ScanInfo] = [] - if vendor_package_pair != []: - # To handle multiple vendors, return all combinations of product/vendor mappings - for v in vendor_package_pair: - vendor = v["vendor"] - file_path = "".join(self.file_stack) - # Tidy up version string - if "^" in version: - version = version[1:] - self.logger.debug(f"{file_path} {product} {version} by {vendor}") - vendorlist.append( - ScanInfo(ProductInfo(vendor, product, version), file_path) - ) - return vendorlist if len(vendorlist) > 0 else None - return None - def run_js_checker(self, filename: str) -> Iterator[ScanInfo]: - """Process package-lock.json file and extract product and dependency details""" - fh = open(filename) - data = json.load(fh) - product = data["name"] - version = data["version"] - vendor = self.find_js_vendor(product, version) - if vendor is not None: - yield from vendor - # Now process dependencies - for i in data["dependencies"]: - # To handle @actions/: lines, extract product name from line - product = i.split("/")[1] if "/" in i else i - # Handle different formats. Either : or - # : { - # ... - # "version" : - # ... - # } - try: - version = data["dependencies"][i]["version"] - except Exception: - # Cater for case when version field not present - version = data["dependencies"][i] - vendor = self.find_js_vendor(product, version) - if vendor is not None: - yield from vendor - if "requires" in data["dependencies"][i]: - for r in data["dependencies"][i]["requires"]: - # To handle @actions/: lines, extract product name from line - product = r.split("/")[1] if "/" in r else r - version = data["dependencies"][i]["requires"][r] - vendor = self.find_js_vendor(product, version) - if vendor is not None: - yield from vendor - self.logger.debug(f"Done scanning file: {filename}") + parser = JavascriptParser(self.cve_db, self.logger) + yield from parser.run_checker(filename) def run_python_package_checkers( self, filename: str, lines: str