Skip to content

feat(vex): integrate lib4vex for VEX document management #5124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cve_bin_tool/vex_manager/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
# Copyright (C) 2024 Intel Corporation
# Copyright (C) 2025 Intel Corporation
# SPDX-License-Identifier: GPL-3.0-or-later

from cve_bin_tool.vex_manager.generate import VEXGenerate
from cve_bin_tool.vex_manager.handler import VexHandler
from cve_bin_tool.vex_manager.parse import VEXParse

__all__ = ["VexHandler", "VEXParse", "VEXGenerate"]
124 changes: 73 additions & 51 deletions cve_bin_tool/vex_manager/generate.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# Copyright (C) 2024 Intel Corporation
# Copyright (C) 2025 Intel Corporation
# SPDX-License-Identifier: GPL-3.0-or-later

from logging import Logger
from pathlib import Path
from typing import Dict, List, Optional

from lib4sbom.data.vulnerability import Vulnerability
from lib4vex.generator import VEXGenerator

from cve_bin_tool.log import LOGGER
from cve_bin_tool.util import CVEData, ProductInfo, Remarks
from cve_bin_tool.vex_manager.handler import VexHandler


class VEXGenerate:
Expand Down Expand Up @@ -103,40 +101,52 @@ def __init__(
self.all_cve_data = all_cve_data
self.sbom_serial_number = sbom_serial_number

# Initialize the VexHandler for generation operations
self.vex_handler = VexHandler(self.logger)

def generate_vex(self) -> None:
"""
Generates a VEX (Vulnerability Exploitability eXchange) document based on the specified VEX type
and stores it in the given filename.

This method sets up a VEX generator instance with the product name, release version, and other
metadata. It automatically assigns a filename if none is provided, logs the update status if the
file already exists, and generates the VEX document with product vulnerability data.
This method delegates to the VexHandler for the actual generation, after preparing the data
structure with product, vulnerabilities and metadata. It automatically assigns a filename if
none is provided and logs update status if the file already exists.

Returns:
None
"""
author = "Unknown Author"
if self.vendor:
author = self.vendor
vexgen = VEXGenerator(vex_type=self.vextype, author=author)
kwargs = {"name": self.product, "release": self.release}
if self.sbom:
kwargs["sbom"] = self.sbom
vexgen.set_product(**kwargs)
if not self.filename:
self.logger.info(
self.logger.debug(
"No filename defined, generating a new filename with default naming convention."
)
self.filename = self.__generate_vex_filename()

if Path(self.filename).is_file():
self.logger.info(f"Updating the VEX file: {self.filename}")
self.logger.debug(f"Updating the VEX file: {self.filename}")

vexgen.generate(
project_name=self.product,
vex_data=self.__get_vulnerabilities(),
metadata=self.__get_metadata(),
filename=self.filename,
)
# Prepare data structure for VexHandler
vex_data = {
"product": {
"name": self.product,
"release": self.release,
"vendor": self.vendor,
},
"project_name": self.product,
"vulnerabilities": self.__get_vulnerabilities(),
"metadata": self.__get_metadata(),
}

# Add SBOM if available
if self.sbom:
vex_data["sbom"] = self.sbom

# Generate VEX document using the handler
success = self.vex_handler.generate(vex_data, self.filename, self.vextype)
if not success:
self.logger.error(f"Failed to generate VEX file: {self.filename}")
else:
self.logger.info(f"Successfully generated VEX file: {self.filename}")

def __generate_vex_filename(self) -> str:
"""
Expand Down Expand Up @@ -183,56 +193,68 @@ def __get_metadata(self) -> Dict:

return metadata

def __get_vulnerabilities(self) -> List[Vulnerability]:
def __get_vulnerabilities(self) -> List[Dict]:
"""
Retrieves and constructs a list of vulnerability objects based on the current CVE data.
Prepares a list of vulnerability dictionaries for the VEX document based on the current CVE data.

This method iterates through all CVE data associated with the product and vendor,
creating and configuring `Vulnerability` objects for each entry. It sets attributes
like name, release, ID, description, status, and additional metadata such as package
URLs (purl) and bill of materials (BOM) links. If a vulnerability includes comments
or justification, these are added to the vulnerability details.
creating vulnerability dictionaries for each entry with attributes like ID, description,
status, and additional metadata such as package URLs (purl) and bill of materials (BOM) links.

Returns:
List[Vulnerability]: A list of `Vulnerability` objects representing the identified
vulnerabilities, enriched with metadata and details.
List[Dict]: A list of vulnerability dictionaries ready for VexHandler to process.
"""
vulnerabilities = []
for product_info, cve_data in self.all_cve_data.items():
vendor, product, version, purl = product_info
vendor = product_info.vendor
product = product_info.product
version = product_info.version
purl = product_info.purl if len(product_info) > 4 else None

for cve in cve_data["cves"]:
if isinstance(cve, str):
continue
vulnerability = Vulnerability(validation=self.vextype)
vulnerability.initialise()
vulnerability.set_name(product)
vulnerability.set_release(version)
vulnerability.set_id(cve.cve_number)
vulnerability.set_description(cve.description)
vulnerability.set_comment(cve.comments)
vulnerability.set_status(self.analysis_state[self.vextype][cve.remarks])
if cve.justification:
vulnerability.set_justification(cve.justification)
if cve.response:
vulnerability.set_value("remediation", cve.response[0])

# Create a vulnerability dictionary in the format expected by VexHandler
vulnerability = {
"name": product,
"release": version,
"id": cve.cve_number,
"description": (
cve.description if hasattr(cve, "description") else ""
),
"comment": cve.comments if hasattr(cve, "comments") else "",
"status": self.analysis_state[self.vextype][cve.remarks],
}

if hasattr(cve, "justification") and cve.justification:
vulnerability["justification"] = cve.justification

if hasattr(cve, "response") and cve.response and len(cve.response) > 0:
vulnerability["remediation"] = cve.response[0]

detail = (
f"{cve.remarks.name}: {cve.comments}"
if cve.comments
if hasattr(cve, "comments") and cve.comments
else cve.remarks.name
)

if purl is None:
purl = f"pkg:generic/{vendor}/{product}@{version}"

bom_version = 1
if self.sbom_serial_number != "":
ref = f"urn:cdx:{self.sbom_serial_number}/{bom_version}#{purl}"
else:
ref = f"urn:cbt:{bom_version}/{vendor}#{product}:{version}"

vulnerability.set_value("purl", str(purl))
vulnerability.set_value("bom_link", ref)
vulnerability.set_value("action", detail)
vulnerability.set_value("source", cve.data_source)
vulnerability.set_value("updated", cve.last_modified)
vulnerabilities.append(vulnerability.get_vulnerability())
vulnerability["purl"] = str(purl)
vulnerability["bom_link"] = ref
vulnerability["action"] = detail
vulnerability["source"] = getattr(cve, "data_source", "unknown")
vulnerability["updated"] = getattr(cve, "last_modified", "")

vulnerabilities.append(vulnerability)

self.logger.debug(f"Vulnerabilities: {vulnerabilities}")
return vulnerabilities
Loading
Loading