From d81ca1163d25c96ef1712c5369c156d8872c1ccc Mon Sep 17 00:00:00 2001 From: Meet Soni <92802561+inosmeet@users.noreply.github.com> Date: Fri, 22 Mar 2024 02:43:20 +0530 Subject: [PATCH] feat: added PURL generation to ruby parser (#3939) Signed-off-by: Meet Soni --- cve_bin_tool/parsers/ruby.py | 50 +++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/cve_bin_tool/parsers/ruby.py b/cve_bin_tool/parsers/ruby.py index b006637915..53d9f03709 100644 --- a/cve_bin_tool/parsers/ruby.py +++ b/cve_bin_tool/parsers/ruby.py @@ -7,10 +7,54 @@ class RubyParser(Parser): + """ + Parser implementation for Ruby gem files (Gemfile.lock). + + This parser is designed to parse Ruby gem 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 Ruby gem 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 = "gem" + + def generate_purl(self, product, version, vendor, qualifier={}, subpath=None): + """Generates PURL after normalizing all components.""" + + product = re.sub(r"^[^a-z]|[^a-z0-9_-]", "", product) + version = re.sub(r"^[^0-9]|[^a-zA-Z0-9.+-]", "", version) + vendor = re.sub(r"^[^a-z]|[^a-z0-9_-]", "", vendor) + + if not re.match(r"^[a-z]|[a-z0-9_-]", product): + return + if vendor == "": + vendor = "UNKNOWN" + if 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(filename) as fh: lines = fh.readlines() @@ -29,7 +73,7 @@ def run_checker(self, filename): ): product = line.strip().split()[0] version = line.strip().split("(")[1][:-1] - vendor = self.find_vendor(product, version) - if vendor is not None: - yield from vendor + vendors = self.find_vendor(product, version) + if vendors is not None: + yield from vendors self.logger.debug(f"Done scanning file: {self.filename}")