diff --git a/cve_bin_tool/parsers/swift.py b/cve_bin_tool/parsers/swift.py index e73ef83c4e..e47b56ad96 100644 --- a/cve_bin_tool/parsers/swift.py +++ b/cve_bin_tool/parsers/swift.py @@ -2,23 +2,80 @@ # SPDX-License-Identifier: GPL-3.0-or-later import json +import re +from json.decoder import JSONDecodeError +from urllib.parse import urlparse from cve_bin_tool.parsers import Parser class SwiftParser(Parser): + """ + Parser implementation for Swift dependency files (Package.resolved). + + This parser is designed to parse Swift dependencies files and generate Package URL (PURL) strings + based on the modules and their dependencies listed in the file. + + Attributes: + cve_db (CVEDB): The CVE database instance used for vulnerability information. + logger (Logger): The logger instance for logging messages and debugging information. + + Methods: + generate_purl(product, version, vendor): + Generates PURL after normalizing all components. + run_checker(filename): + Parse the Swift dependency file and yield valid PURLs for the modules listed in the file. + + """ + def __init__(self, cve_db, logger): super().__init__(cve_db, logger) + self.purl_pkg_type = "swift" + + def generate_purl(self, product, version, vendor, qualifier={}, subpath=None): + """Generates PURL after normalizing all components.""" + + product = re.sub(r"[^a-zA-Z0-9_-]", "", product) + version = re.sub(r"[^a-zA-Z0-9.+-]", "", version) + + if not re.match(r"[a-zA-Z0-9_-]", product): + return + if not vendor: + vendor = "UNKNOWN" + if not version: + version = "UNKNOWN" + + purl = super().generate_purl( + product, + version, + vendor, + qualifier, + subpath, + ) + + return purl def run_checker(self, filename): + """Parse the file and yield valid PURLs.""" self.filename = filename with open(self.filename) as fh: # parse the json structure for extracting product version pairs - content = json.load(fh) + try: + content = json.load(fh) + except JSONDecodeError as e: + self.logger.debug(f"Error occurred while parsing {filename}: {e}") + return for package in content["object"]["pins"]: product = package["package"] version = package["state"]["version"] - vendor = self.find_vendor(product, version) - if vendor is not None: - yield from vendor + repository_url = package.get("repositoryURL", None) + domain = None + if repository_url: + parse = urlparse(repository_url) + domain = parse.netloc + self.logger.debug(domain) + + vendors = self.find_vendor(product, version) + if vendors is not None: + yield from vendors self.logger.debug(f"Done scanning file: {self.filename}")