Skip to content

Commit 96ff61b

Browse files
authored
fix: drop location handling (intel#4822)
Drop find_product_location as it tries to find the location of the product on the system parsing the SBOM which obviously doesn't make sense While at it, drop location totally from ProductInfo. Indeed, currently only the first location of the product is saved in the SBOM. Instead of using this location field, set the evidence to be the list of paths saved in all_cve_data. This will avoid to duplicate information. __identity_members, __eq__ and __hash__, which were added by commit f1d3c75 to handle location, are kept to still handle the optional purl parameter and avoid breaking test_product_info_{equality,hashing} tests. Fix intel#4676 Signed-off-by: Fabrice Fontaine <[email protected]>
1 parent 7ed8599 commit 96ff61b

24 files changed

+63
-291
lines changed

cve_bin_tool/input_engine.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ def parse_data(self, fields: Set[str], data: Iterable) -> None:
158158
row["vendor"].strip(),
159159
row["product"].strip(),
160160
row["version"].strip(),
161-
row.get("location", "location/to/product").strip(),
162161
)
163162
self.parsed_data[product_info][
164163
row.get("cve_number", "").strip() or "default"

cve_bin_tool/merge.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,6 @@ def parse_data_from_json(
211211
row["vendor"].strip(),
212212
row["product"].strip(),
213213
row["version"].strip(),
214-
row.get("location", "location/to/product").strip(),
215214
)
216215
parsed_data[product_info][row.get("cve_number", "").strip() or "default"] = {
217216
"remarks": Remarks(str(row.get("remarks", "")).strip()),

cve_bin_tool/output_engine/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ def output_csv(
9090
"vendor",
9191
"product",
9292
"version",
93-
"location",
9493
"cve_number",
9594
"severity",
9695
"score",
@@ -813,6 +812,7 @@ def output_cves(self, outfile, output_type="console"):
813812
self.sbom_filename = add_extension_if_not(self.sbom_filename, "json")
814813
sbomgen = SBOMGenerate(
815814
self.all_product_data,
815+
self.all_cve_data,
816816
self.sbom_filename,
817817
self.sbom_type,
818818
self.sbom_format,

cve_bin_tool/output_engine/util.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ def format_output(
160160
"vendor": "haxx"
161161
"product": "curl",
162162
"version": "1.2.1",
163-
"location": "/usr/local/bin/product",
164163
"cve_number": "CVE-1234-1234",
165164
"severity": "LOW",
166165
"score": "1.2",
@@ -192,7 +191,6 @@ def format_output(
192191
"vendor": product_info.vendor,
193192
"product": product_info.product,
194193
"version": product_info.version,
195-
"location": product_info.location,
196194
"cve_number": cve.cve_number,
197195
"severity": cve.severity,
198196
"score": str(cve.score),

cve_bin_tool/parsers/__init__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,18 @@ def find_vendor(self, product, version):
7373
vendor_package_pair = self.cve_db.get_vendor_product_pairs(product)
7474
vendorlist: list[ScanInfo] = []
7575
file_path = self.filename
76-
location = file_path
7776
if vendor_package_pair != []:
7877
# To handle multiple vendors, return all combinations of product/vendor mappings
7978
for v in vendor_package_pair:
8079
vendor = v["vendor"]
81-
location = v.get("location", self.filename)
8280
self.logger.debug(f"{file_path} {product} {version} by {vendor}")
8381
vendorlist.append(
84-
ScanInfo(ProductInfo(vendor, product, version, location), file_path)
82+
ScanInfo(ProductInfo(vendor, product, version), file_path)
8583
)
8684
else:
8785
# Add entry
8886
vendorlist.append(
89-
ScanInfo(ProductInfo("UNKNOWN", product, version, location), file_path)
87+
ScanInfo(ProductInfo("UNKNOWN", product, version), file_path)
9088
)
9189
return vendorlist
9290

@@ -150,7 +148,6 @@ def find_vendor_from_purl(self, purl, ver) -> tuple[list[ScanInfo], bool]:
150148
vendor,
151149
product,
152150
ver,
153-
self.filename,
154151
purl_with_ver,
155152
),
156153
self.filename,

cve_bin_tool/parsers/env.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ class EnvNamespaceConfig:
1818
"""
1919
Configuration details for environment namespace in the CVE Bin tool
2020
Attributes:
21-
CVE ID associated with this namespace, vendor name, product name, version of the product, file path where product is located
21+
CVE ID associated with this namespace, vendor name, product name, version of the product
2222
"""
2323

2424
ad_hoc_cve_id: str
2525
vendor: str
2626
product: str
2727
version: str
28-
location: str = "/usr/local/bin/product"
2928

3029

3130
@dataclasses.dataclass
@@ -139,7 +138,6 @@ def run_checker(self, filename):
139138
cve.vendor,
140139
cve.product,
141140
cve.version,
142-
cve.location,
143141
PackageURL(
144142
type="ad-hoc",
145143
namespace=cve.vendor,

cve_bin_tool/parsers/java.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,8 @@ def find_vendor(self, product, version):
5858
for pair in vendor_package_pair:
5959
vendor = pair["vendor"]
6060
file_path = self.filename
61-
location = pair.get("location", self.filename)
6261
self.logger.debug(f"{file_path} {product} {version} by {vendor}")
63-
info.append(
64-
ScanInfo(ProductInfo(vendor, product, version, location), file_path)
65-
)
62+
info.append(ScanInfo(ProductInfo(vendor, product, version), file_path))
6663
return info
6764
return None
6865

cve_bin_tool/sbom_manager/generate.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ class SBOMGenerate:
2525
def __init__(
2626
self,
2727
all_product_data,
28+
all_cve_data,
2829
filename="",
2930
sbom_type="spdx",
3031
sbom_format="tag",
3132
sbom_root="CVE-SCAN",
3233
logger: Optional[Logger] = None,
3334
):
3435
self.all_product_data = all_product_data
36+
self.all_cve_data = all_cve_data
3537
self.filename = filename
3638
self.sbom_type = sbom_type
3739
self.sbom_format = sbom_format
@@ -81,8 +83,9 @@ def generate_sbom(self) -> None:
8183
in self.sbom_packages
8284
and product_data.vendor == "unknown"
8385
):
84-
location = product_data.location
85-
my_package.set_evidence(location) # Set location directly
86+
if self.all_cve_data.get(product_data):
87+
for path in self.all_cve_data[product_data]["paths"]:
88+
my_package.set_evidence(path)
8689
self.sbom_packages[
8790
(my_package.get_name(), my_package.get_value("version"))
8891
] = my_package.get_package()

cve_bin_tool/sbom_manager/parse.py

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
Remarks,
2222
decode_cpe22,
2323
decode_cpe23,
24-
find_product_location,
25-
validate_location,
2624
validate_serialNumber,
2725
)
2826
from cve_bin_tool.validator import validate_cyclonedx, validate_spdx, validate_swid
@@ -101,21 +99,9 @@ def parse_sbom(self) -> dict[ProductInfo, TriageData]:
10199
vendor_set = self.get_vendor(product)
102100
for vendor in vendor_set:
103101
# if vendor is not None:
104-
location = find_product_location(product)
105-
if location is None:
106-
location = "NotFound"
107-
if validate_location(location) is False:
108-
raise ValueError(f"Invalid location {location} for {product}")
109-
parsed_data.append(ProductInfo(vendor, product, version, location))
102+
parsed_data.append(ProductInfo(vendor, product, version))
110103
else:
111-
location = find_product_location(product)
112-
if location is None:
113-
location = "NotFound"
114-
if validate_location(location) is False:
115-
raise ValueError(f"Invalid location {location} for {product}")
116-
parsed_data.append(
117-
ProductInfo(module_vendor, product, version, location)
118-
)
104+
parsed_data.append(ProductInfo(module_vendor, product, version))
119105

120106
for row in parsed_data:
121107
self.sbom_data[row]["default"] = {
@@ -152,17 +138,10 @@ def common_prefix_split(self, product, version) -> list[ProductInfo]:
152138
len(common_prefix_vendor) == 1
153139
and common_prefix_vendor[0] != "UNKNOWN"
154140
):
155-
location = find_product_location(common_prefix_product)
156-
if location is None:
157-
location = "NotFound"
158-
if validate_location(location) is False:
159-
raise ValueError(f"Invalid location {location} for {product}")
160141
found_common_prefix = True
161142
for vendor in common_prefix_vendor:
162143
parsed_data.append(
163-
ProductInfo(
164-
vendor, common_prefix_product, version, location
165-
)
144+
ProductInfo(vendor, common_prefix_product, version)
166145
)
167146
break
168147
if not found_common_prefix:
@@ -176,15 +155,8 @@ def common_prefix_split(self, product, version) -> list[ProductInfo]:
176155
temp = self.get_vendor(sp)
177156
if len(temp) > 1 or (len(temp) == 1 and temp[0] != "UNKNOWN"):
178157
for vendor in temp:
179-
location = find_product_location(sp)
180-
if location is None:
181-
location = "NotFound"
182-
if validate_location(location) is False:
183-
raise ValueError(
184-
f"Invalid location {location} for {product}"
185-
)
186158
# if vendor is not None:
187-
parsed_data.append(ProductInfo(vendor, sp, version, location))
159+
parsed_data.append(ProductInfo(vendor, sp, version))
188160
return parsed_data
189161

190162
def get_vendor(self, product: str) -> list:

cve_bin_tool/util.py

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -156,23 +156,20 @@ class ProductInfo(NamedTuple):
156156
vendor: str
157157
product: str
158158
version: str
159-
location: str
160159
purl: Optional[str]
161160
"""
162161

163162
vendor: str
164163
product: str
165164
version: str
166-
location: str
167165
purl: str | None = None
168166

169167
def __identity_members(self):
170168
"""The members that will be used for eq and hash implementations.
171-
We do not include location here since it can take on different values
169+
We do not include purl here since it can take on different values
172170
depending on where the product info is coming from and we want to be
173171
able to properly identify products that are actually the same.
174172
"""
175-
# TODO: what is the meaning of the location field exactly?
176173
return (self.vendor, self.product, self.version)
177174

178175
def __eq__(self, other):
@@ -308,52 +305,6 @@ def make_http_requests(attribute, **kwargs):
308305
LOGGER.error(ve)
309306

310307

311-
def find_product_location(product_name):
312-
"""
313-
Find the location of a product in the system.
314-
Returns the location of the product if found, None otherwise.
315-
"""
316-
for path in sys.path:
317-
product_location = Path(path) / product_name
318-
if product_location.exists():
319-
return str(product_location)
320-
parts = product_name.split("-")
321-
for part in parts:
322-
product_location = Path(path) / part
323-
if product_location.exists():
324-
return str(product_location)
325-
326-
known_installation_directories = [
327-
"/usr/local/bin",
328-
"/usr/local/sbin",
329-
"/usr/bin",
330-
"/opt",
331-
"/usr/sbin",
332-
"/usr/local/lib",
333-
"/usr/lib",
334-
"/usr/local/share",
335-
"/usr/share",
336-
"/usr/local/include",
337-
"/usr/include",
338-
]
339-
340-
for directory in known_installation_directories:
341-
product_location = Path(directory) / product_name
342-
if product_location.exists():
343-
return str(product_location)
344-
345-
return None
346-
347-
348-
def validate_location(location: str) -> bool:
349-
"""
350-
Validates the location.
351-
Returns True if the location is valid, False otherwise.
352-
"""
353-
pattern = r"^(?!https?:\/\/)(?=.*[\\/])(?!.*@)[a-zA-Z0-9_\-\\\/\s]+|NotFound$"
354-
return bool(re.match(pattern, location))
355-
356-
357308
def decode_purl(purl: str) -> ProductInfo | None:
358309
"""
359310
Decode a Package URL (purl) in the format: pkg:type/namespace/product@version.
@@ -363,9 +314,8 @@ def decode_purl(purl: str) -> ProductInfo | None:
363314
364315
Returns:
365316
- ProductInfo | None: An instance of ProductInfo containing the vendor, product,
366-
version, location, and purl, or None if the purl is invalid.
317+
version, and purl, or None if the purl is invalid.
367318
"""
368-
location = "location/to/product"
369319

370320
try:
371321
purl_obj = PackageURL.from_string(purl)
@@ -377,7 +327,6 @@ def decode_purl(purl: str) -> ProductInfo | None:
377327
vendor=vendor,
378328
product=product,
379329
version=version,
380-
location=location,
381330
purl=purl,
382331
)
383332
return product_info
@@ -421,7 +370,6 @@ def decode_bom_ref(ref: str):
421370
urn_cdx_with_purl = re.compile(
422371
r"urn:cdx:(?P<bomSerialNumber>[^/]+)\/(?P<bom_version>[^#]+)#(?P<purl>pkg:[^\s]+)"
423372
)
424-
location = "location/to/product"
425373
match = (
426374
urn_cdx_with_purl.match(ref)
427375
or urn_cbt_ext_ref.match(ref)
@@ -456,9 +404,7 @@ def decode_bom_ref(ref: str):
456404

457405
if product and vendor and version:
458406
if validate_product_vendor(product, vendor) and validate_version(version):
459-
return ProductInfo(
460-
vendor.strip(), product.strip(), version.strip(), location
461-
)
407+
return ProductInfo(vendor.strip(), product.strip(), version.strip())
462408

463409
return None
464410

cve_bin_tool/vex_manager/generate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def __get_vulnerabilities(self) -> List[Vulnerability]:
199199
"""
200200
vulnerabilities = []
201201
for product_info, cve_data in self.all_cve_data.items():
202-
vendor, product, version, _, purl = product_info
202+
vendor, product, version, purl = product_info
203203
for cve in cve_data["cves"]:
204204
if isinstance(cve, str):
205205
continue

test/test_available_fix.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ def test_redhat_available_fix_output(
156156
vendor="gnu",
157157
product="pspp",
158158
version="1.2.0",
159-
location="/usr/local/bin/pspp",
160159
): CVEData(
161160
None,
162161
{
@@ -185,7 +184,6 @@ def test_redhat_available_fix_output(
185184
vendor="avahi",
186185
product="avahi",
187186
version="0.6.25",
188-
location="/usr/local/bin/avahi",
189187
): CVEData(
190188
None,
191189
{
@@ -235,7 +233,6 @@ def test_redhat_available_fix_output(
235233
vendor="nodejs",
236234
product="node.js",
237235
version="14.16.0",
238-
location="/usr/local/bin/nodejs",
239236
): CVEData(
240237
None,
241238
{

test/test_cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ def test_basic_epss(self, caplog):
546546
csv_rows = content.splitlines()
547547
assert len(csv_rows) > 0
548548
# row 0 is the header,
549-
# vendor,product,version,location,cve_number,severity,score,source,cvss_version,cvss_vector,paths,
549+
# vendor,product,version,cve_number,severity,score,source,cvss_version,cvss_vector,paths,
550550
# remarks,comments,epss_probability,epss_percentile
551551
row_zero = csv_rows[0].split(",")
552552
# row 1 should contain some EPSS values
@@ -558,7 +558,7 @@ def test_basic_epss(self, caplog):
558558
"last header value in produced csv file must be " "'epss_percentile'"
559559
)
560560

561-
assert len(row_one) == 15, "one csv row should have 15 values"
561+
assert len(row_one) == 14, "one csv row should have 14 values"
562562
assert (
563563
0.0 <= float(row_one[-1]) <= 1.0
564564
), "last value in the row must be the epss percentile value, i.e., a floating point between 0.0 and 1.0"

0 commit comments

Comments
 (0)