Skip to content

Commit

Permalink
feat: added a function to utilize purl integration (intel#4164)
Browse files Browse the repository at this point in the history
modified python language parser, tailored purls according to the
purl2cpe requirement, resolves docutils name collision

Signed-off-by: Meet Soni <meetsoni3017@gmail.com>
  • Loading branch information
inosmeet committed Jun 11, 2024
1 parent 94e5a2d commit e9f1ea8
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 11 deletions.
65 changes: 65 additions & 0 deletions cve_bin_tool/parsers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Copyright (C) 2022 Intel Corporation
# SPDX-License-Identifier: GPL-3.0-or-later

import re
import sqlite3
from pathlib import Path
from typing import List, Tuple

from packageurl import PackageURL

from cve_bin_tool.error_handler import CVEDBError
from cve_bin_tool.util import ProductInfo, ScanInfo

__all__ = [
Expand Down Expand Up @@ -89,3 +95,62 @@ def generate_purl(self, product, vendor, qualifier={}, subpath=None):
subpath=subpath,
)
return purl

def find_vendor_from_purl(self, purl, ver) -> Tuple[List[ScanInfo], bool]:
"""
Finds the vendor information for a given PackageURL (purl) and version from the database.
This method queries the database to retrieve Common Platform Enumeration (CPE) data associated with the given purl.
It then decodes the CPE data to extract vendor, product, and version information. If the version matches the provided
version, it constructs a ScanInfo object for each matching entry and returns a list of these objects.
"""

query = "SELECT cpe from purl2cpe WHERE purl=?"
cursor = self.db_open_and_get_cursor()
cursor.execute(query, [str(purl)])
cpeList = cursor.fetchall()
vendorlist: list[ScanInfo] = []
vendors = set()

if cpeList != []:
for item in cpeList:
vendor, product, version = self.decode_cpe23(str(item))
vendors.add((vendor, product))
else:
return vendorlist, False

for vendor, product in vendors:
vendorlist.append(
ScanInfo(
ProductInfo(vendor, product, ver, "/usr/local/bin/product"),
self.filename,
)
)

return vendorlist, True

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

dbpath = (
Path("~").expanduser() / ".cache" / "cve-bin-tool" / "purl2cpe/purl2cpe.db"
)
connection = sqlite3.connect(dbpath)

if connection is not None:
cursor = connection.cursor()
if cursor is None:
raise CVEDBError
return cursor

def decode_cpe23(self, cpe23) -> Tuple[str, str, str]:
"""
Decodes a CPE 2.3 formatted string to extract vendor, product, and version information.
"""

# split on `:` only if it's not escaped
cpe = re.split(r"(?<!\\):", cpe23)
vendor, product, version = cpe[3], cpe[4], cpe[5]
# Return available data, convert empty fields to None
return (vendor, product, version)
36 changes: 25 additions & 11 deletions cve_bin_tool/parsers/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ def run_checker(self, filename):
for line in lines["install"]:
product = line["metadata"]["name"]
version = line["metadata"]["version"]
vendor = self.find_vendor(product, version)
purl = self.generate_purl(product, "")
vendor, result = self.find_vendor_from_purl(purl, version)

if not result:
vendor = self.find_vendor(product, version)

if vendor is not None:
yield from vendor
Expand Down Expand Up @@ -143,16 +147,26 @@ def run_checker(self, filename):
try:
product = search(compile(r"^Name: (.+)$", MULTILINE), lines).group(1)
version = search(compile(r"^Version: (.+)$", MULTILINE), lines).group(1)
vendor_package_pair = self.cve_db.get_vendor_product_pairs(product)
if vendor_package_pair != []:
for pair in vendor_package_pair:
vendor = pair["vendor"]
location = pair.get("location", "/usr/local/bin/product")
file_path = self.filename
self.logger.debug(f"{file_path} is {vendor}.{product} {version}")
yield ScanInfo(
ProductInfo(vendor, product, version, location), file_path
)
purl = self.generate_purl(product, "")
vendor, result = self.find_vendor_from_purl(purl, version)

if vendor is not None:
yield from vendor

if not result:
vendor_package_pair = self.cve_db.get_vendor_product_pairs(product)
if vendor_package_pair != []:
for pair in vendor_package_pair:
vendor = pair["vendor"]
location = pair.get("location", "/usr/local/bin/product")
file_path = self.filename
self.logger.debug(
f"{file_path} is {vendor}.{product} {version}"
)
yield ScanInfo(
ProductInfo(vendor, product, version, location), file_path
)

# There are packages with a METADATA file in them containing different data from what the tool expects
except AttributeError:
self.logger.debug(f"{filename} is an invalid METADATA/PKG-INFO")
Expand Down

0 comments on commit e9f1ea8

Please sign in to comment.