Skip to content

Commit c794765

Browse files
authored
refactor: modified language parsers for purl2cpe support (#4188)
changed all the language parsers according to the purl2cpe database and made the database query universal for all parsers removed 'slf4j-simple' and 'slf4j-api' products from fail_pom.xml file as they can be found now with purl2cpe Signed-off-by: Meet Soni <[email protected]>
1 parent a34c43c commit c794765

File tree

14 files changed

+137
-90
lines changed

14 files changed

+137
-90
lines changed

cve_bin_tool/parsers/__init__.py

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def find_vendor(self, product, version):
8585
)
8686
return vendorlist
8787

88-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
88+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
8989
"""Generate purl string based on various components."""
9090
purl = PackageURL(
9191
type=self.purl_pkg_type,
@@ -104,36 +104,48 @@ def find_vendor_from_purl(self, purl, ver) -> Tuple[List[ScanInfo], bool]:
104104
It then decodes the CPE data to extract vendor, product, and version information. If the version matches the provided
105105
version, it constructs a ScanInfo object for each matching entry and returns a list of these objects.
106106
"""
107-
108-
query = "SELECT cpe from purl2cpe WHERE purl=?"
109-
cursor = self.db_open_and_get_cursor()
110-
cursor.execute(query, [str(purl)])
111-
cpeList = cursor.fetchall()
112-
vendorlist: list[ScanInfo] = []
113-
vendors = set()
114-
115-
if cpeList != []:
116-
for item in cpeList:
117-
vendor, product, version = self.decode_cpe23(str(item))
118-
vendors.add((vendor, product))
119-
else:
120-
return vendorlist, False
121-
purl_with_ver = f"{str(purl)}@{ver}"
122-
for vendor, product in vendors:
123-
vendorlist.append(
124-
ScanInfo(
125-
ProductInfo(
126-
vendor,
127-
product,
128-
ver,
129-
"/usr/local/bin/product",
130-
purl=purl_with_ver,
131-
),
132-
self.filename,
107+
try:
108+
purl = purl.to_dict()
109+
param1 = f"pkg:{purl['type']}/{purl['name']}"
110+
param2 = f"pkg:{purl['type']}/%/{purl['name']}"
111+
112+
query = """
113+
SELECT cpe from purl2cpe WHERE purl LIKE ?
114+
UNION
115+
SELECT cpe from purl2cpe WHERE purl LIKE ?
116+
"""
117+
cursor = self.db_open_and_get_cursor()
118+
cursor.execute(query, (param1, param2))
119+
cpeList = cursor.fetchall()
120+
vendorlist: list[ScanInfo] = []
121+
vendors = set()
122+
123+
if cpeList != []:
124+
for item in cpeList:
125+
vendor, _, _ = self.decode_cpe23(str(item))
126+
vendors.add((vendor, purl["name"]))
127+
else:
128+
return vendorlist, False
129+
130+
purl_with_ver = f"{str(purl)}@{ver}"
131+
for vendor, product in vendors:
132+
vendorlist.append(
133+
ScanInfo(
134+
ProductInfo(
135+
vendor,
136+
product,
137+
ver,
138+
"/usr/local/bin/product",
139+
purl_with_ver,
140+
),
141+
self.filename,
142+
)
133143
)
134-
)
135144

136-
return vendorlist, True
145+
return vendorlist, True
146+
except Exception as e:
147+
self.logger.error(f"Error occurred: {e}")
148+
return [], False
137149

138150
def db_open_and_get_cursor(self) -> sqlite3.Cursor:
139151
"""Opens connection to sqlite database, returns cursor object."""

cve_bin_tool/parsers/dart.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@ def __init__(self, cve_db, logger):
1919
super().__init__(cve_db, logger)
2020
self.purl_pkg_type = "pub"
2121

22-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
22+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
2323
"""
2424
Generates PURL after normalizing all components.
2525
pubspec: https://dart.dev/tools/pub/pubspec#name
2626
purl-spec for pub: https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#pub
2727
"""
28-
# Normalize product and vendor for Dart packages
28+
# Normalize product for Dart packages
2929
product = re.sub(r"[^a-zA-Z0-9_]", "", product).lower()
30-
vendor = "UNKNOWN" # The vendor is not explicitly defined for pub packages
3130
if not product:
3231
return None
3332

@@ -50,7 +49,10 @@ def run_checker(self, filename):
5049
for package_name, package_detail in data.get("packages", {}).items():
5150
product = package_name
5251
version = package_detail.get("version").replace('"', "")
53-
vendor = self.find_vendor(product, version)
52+
purl = self.generate_purl(product)
53+
vendor, result = self.find_vendor_from_purl(purl, version)
54+
if not result:
55+
vendor = self.find_vendor(product, version)
5456
if vendor:
5557
yield from vendor
5658
self.logger.debug(f"Done scanning file: {self.filename}")

cve_bin_tool/parsers/go.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,13 @@ def __init__(self, cve_db, logger):
2929
super().__init__(cve_db, logger)
3030
self.purl_pkg_type = "golang"
3131

32-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
32+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
3333
"""Generates PURL after normalizing all components."""
3434

3535
product = re.sub(r"[^a-zA-Z0-9_-]", "", product)
36-
vendor = re.sub(r"^[^a-zA-Z_]|[^a-zA-Z0-9_-]", "", vendor)
3736

3837
if not re.match(r"^[a-zA-Z0-9_-]", product):
3938
return
40-
if vendor == "":
41-
vendor = "UNKNOWN"
4239

4340
purl = super().generate_purl(
4441
product,
@@ -73,7 +70,11 @@ def run_checker(self, filename):
7370
if len(parts) >= 2:
7471
product = line.split(" ")[0].split("/")[-1]
7572
version = line.split(" ")[1][1:].split("-")[0].split("+")[0]
76-
vendors = self.find_vendor(product, version)
73+
purl = self.generate_purl(product)
74+
vendors, result = self.find_vendor_from_purl(purl, version)
75+
76+
if not result:
77+
vendors = self.find_vendor(product, version)
7778
if vendors is not None:
7879
yield from vendors
7980
self.logger.debug(f"Done scanning file: {self.filename}")

cve_bin_tool/parsers/java.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# NOTE: DONE
12
# Copyright (C) 2022 Intel Corporation
23
# SPDX-License-Identifier: GPL-3.0-or-later
34
"""Script containing all functionalities relating to parsing of Java-based files."""
@@ -18,11 +19,10 @@ def __init__(self, cve_db, logger, validate=True):
1819
self.validate = validate
1920
self.purl_pkg_type = "maven"
2021

21-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
22+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
2223
"""Generates PURL after normalizing all components of a Maven package."""
23-
# Normalize product and vendor
24+
# Normalize product
2425
product = re.sub(r"[^a-zA-Z0-9._-]", "", product).lower()
25-
vendor = re.sub(r"[^a-zA-Z0-9._-]", "", vendor).lower() if vendor else "UNKNOWN"
2626

2727
if not product:
2828
return None
@@ -97,7 +97,10 @@ def run_checker(self, filename):
9797
if product is None and parent is not None:
9898
product = parent.find(schema + "artifactId").text
9999
if product is not None and version is not None:
100-
product_info = self.find_vendor(product, version)
100+
purl = self.generate_purl(product)
101+
product_info, result = self.find_vendor_from_purl(purl, version)
102+
if not result:
103+
product_info = self.find_vendor(product, version)
101104
if product_info is not None:
102105
yield from product_info
103106

@@ -130,7 +133,14 @@ def run_checker(self, filename):
130133
self.logger.debug(f"{file_path} {product.text} {version}")
131134
if version[0].isdigit():
132135
# Valid version identifier
133-
product_info = self.find_vendor(product.text, version)
136+
purl = self.generate_purl(product.text)
137+
product_info, result = self.find_vendor_from_purl(
138+
purl, version
139+
)
140+
if not result:
141+
product_info = self.find_vendor(
142+
product.text, version
143+
)
134144
if product_info is not None:
135145
yield from product_info
136146
self.logger.debug(f"Done scanning file: {filename}")

cve_bin_tool/parsers/javascript.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ def __init__(self, cve_db, logger):
1515
super().__init__(cve_db, logger)
1616
self.purl_pkg_type = "npm"
1717

18-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
18+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
1919
"""Generates PURL after normalizing all components."""
2020
product = re.sub(r"[^a-zA-Z0-9._-]", "", product).lower()
21-
vendor = "UNKNOWN" # Typically, the vendor is not explicitly defined for npm packages
2221

2322
if not product:
2423
return None
@@ -44,7 +43,11 @@ def run_checker(self, filename):
4443
if "name" in data and "version" in data:
4544
product = data["name"]
4645
version = data["version"]
47-
vendor = self.find_vendor(product, version)
46+
purl = self.generate_purl(product)
47+
vendor, result = self.find_vendor_from_purl(purl, version)
48+
49+
if not result:
50+
vendor = self.find_vendor(product, version)
4851
else:
4952
vendor = None
5053
if vendor is not None:
@@ -93,7 +96,11 @@ def run_checker(self, filename):
9396
product_version_mapping.append((product, version))
9497

9598
for product, version in product_version_mapping:
96-
vendor = self.find_vendor(product, version)
99+
purl = self.generate_purl(product, "")
100+
vendor, result = self.find_vendor_from_purl(purl, version)
101+
102+
if not result:
103+
vendor = self.find_vendor(product, version)
97104
if vendor is not None:
98105
yield from vendor
99106
self.logger.debug(f"Done scanning file: {self.filename}")

cve_bin_tool/parsers/perl.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ def __init__(self, cve_db, logger):
1313
super().__init__(cve_db, logger)
1414
self.purl_pkg_type = "cpan"
1515

16-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
16+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
1717
"""Generates PURL after normalizing all components."""
1818
# Normalize product and vendor for Perl packages
1919
product = re.sub(r"[^a-zA-Z0-9._-]", "", product).lower()
20-
vendor = "UNKNOWN" # Typically, the vendor is not explicitly defined for CPAN packages
2120

2221
if not product:
2322
return None
@@ -53,7 +52,13 @@ def run_checker(self, filename):
5352

5453
# Print the extracted dependencies
5554
for dependency in dependencies:
56-
vendor = self.find_vendor(dependency[0], dependency[1])
55+
product = dependency[0]
56+
version = dependency[1]
57+
purl = self.generate_purl(product)
58+
vendor, result = self.find_vendor_from_purl(purl, version)
59+
60+
if not result:
61+
vendor = self.find_vendor(product, version)
5762
if vendor is not None:
5863
yield from vendor
5964
self.logger.debug(f"Done scanning file: {self.filename}")

cve_bin_tool/parsers/php.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# NOTE: remains not complete
2+
3+
14
# Copyright (C) 2024 Intel Corporation
25
# SPDX-License-Identifier: GPL-3.0-or-later
36
"""Python script containing all functionalities related to parsing of php's composer.lock files."""
@@ -19,12 +22,11 @@ def __init__(self, cve_db, logger):
1922
super().__init__(cve_db, logger)
2023
self.purl_pkg_type = "composer"
2124

22-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
25+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
2326
"""Generates PURL after normalizing all components."""
24-
vendor = re.sub(r"[^a-zA-Z0-9._-]", "", vendor).lower()
2527
product = re.sub(r"[^a-zA-Z0-9._-]", "", product).lower()
2628

27-
if not vendor or not product:
29+
if not product:
2830
return None
2931

3032
purl = super().generate_purl(
@@ -51,7 +53,11 @@ def run_checker(self, filename):
5153
version = version[1:]
5254
if "dev" in version:
5355
continue
54-
vendor = self.find_vendor(product, version)
56+
purl = self.generate_purl(product)
57+
vendor, result = self.find_vendor_from_purl(purl, version)
58+
59+
if not result:
60+
vendor = self.find_vendor(product, version)
5561
if vendor is not None:
5662
yield from vendor
5763
self.logger.debug(f"Done scanning file: {self.filename}")

cve_bin_tool/parsers/python.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def __init__(self, cve_db, logger):
2525
super().__init__(cve_db, logger)
2626
self.purl_pkg_type = "pypi"
2727

28-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
28+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
2929
"""Generates PURL after normalizing all components."""
3030
product = re.sub(r"[^a-zA-Z0-9._-]", "", product).lower()
3131

@@ -96,7 +96,7 @@ def run_checker(self, filename):
9696
for line in lines["install"]:
9797
product = line["metadata"]["name"]
9898
version = line["metadata"]["version"]
99-
purl = self.generate_purl(product, "")
99+
purl = self.generate_purl(product)
100100
vendor, result = self.find_vendor_from_purl(purl, version)
101101

102102
if not result:
@@ -119,7 +119,7 @@ def __init__(self, cve_db, logger):
119119
super().__init__(cve_db, logger)
120120
self.purl_pkg_type = "pypi"
121121

122-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
122+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
123123
"""Generates PURL after normalizing all components."""
124124
product = re.sub(r"[^a-zA-Z0-9._-]", "", product).lower()
125125

@@ -147,7 +147,7 @@ def run_checker(self, filename):
147147
try:
148148
product = search(compile(r"^Name: (.+)$", MULTILINE), lines).group(1)
149149
version = search(compile(r"^Version: (.+)$", MULTILINE), lines).group(1)
150-
purl = self.generate_purl(product, "")
150+
purl = self.generate_purl(product)
151151
vendor, result = self.find_vendor_from_purl(purl, version)
152152

153153
if vendor is not None:

cve_bin_tool/parsers/r.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@ def __init__(self, cve_db, logger):
3030
super().__init__(cve_db, logger)
3131
self.purl_pkg_type = "cran"
3232

33-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
33+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
3434
"""Generates PURL after normalizing all components."""
3535

3636
product = re.sub(r"[^a-zA-Z0-9.-]", "", product)
37-
vendor = "UNKNOWN"
3837

3938
if not re.match(r"^[a-zA-Z0-9_-]", product):
4039
return
@@ -57,7 +56,12 @@ def run_checker(self, filename):
5756
for package in content["Packages"]:
5857
product = content["Packages"][package]["Package"]
5958
version = content["Packages"][package]["Version"]
60-
vendor = self.find_vendor(product, version)
59+
purl = self.generate_purl(product)
60+
vendor, result = self.find_vendor_from_purl(purl, version)
61+
62+
if not result:
63+
vendor = self.find_vendor(product, version)
64+
6165
if vendor is not None:
6266
yield from vendor
6367
self.logger.debug(f"Done scanning file: {self.filename}")

cve_bin_tool/parsers/ruby.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# Copyright (C) 2022 Intel Corporation
1+
# Copyright (C) 2024 Intel Corporation
2+
3+
24
# SPDX-License-Identifier: GPL-3.0-or-later
35

46
import re
@@ -29,16 +31,13 @@ def __init__(self, cve_db, logger):
2931
super().__init__(cve_db, logger)
3032
self.purl_pkg_type = "gem"
3133

32-
def generate_purl(self, product, vendor, qualifier={}, subpath=None):
34+
def generate_purl(self, product, vendor="", qualifier={}, subpath=None):
3335
"""Generates PURL after normalizing all components."""
3436

3537
product = re.sub(r"^[^a-z]|[^a-z0-9_-]", "", product)
36-
vendor = re.sub(r"^[^a-z]|[^a-z0-9_-]", "", vendor)
3738

3839
if not re.match(r"^[a-z]|[a-z0-9_-]", product):
3940
return
40-
if vendor == "":
41-
vendor = "UNKNOWN"
4241

4342
purl = super().generate_purl(
4443
product,
@@ -69,7 +68,11 @@ def run_checker(self, filename):
6968
):
7069
product = line.strip().split()[0]
7170
version = line.strip().split("(")[1][:-1]
72-
vendors = self.find_vendor(product, version)
71+
purl = self.generate_purl(product)
72+
vendors, result = self.find_vendor_from_purl(purl, version)
73+
74+
if not result:
75+
vendors = self.find_vendor(product, version)
7376
if vendors is not None:
7477
yield from vendors
7578
self.logger.debug(f"Done scanning file: {self.filename}")

0 commit comments

Comments
 (0)