Skip to content

Commit fef87d2

Browse files
committed
feat(vex): Refactered generate.py and fixed tests
1 parent fc7c159 commit fef87d2

File tree

3 files changed

+349
-57
lines changed

3 files changed

+349
-57
lines changed

cve_bin_tool/vex_manager/generate.py

Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
# Copyright (C) 2024 Intel Corporation
1+
# Copyright (C) 2025 Intel Corporation
22
# SPDX-License-Identifier: GPL-3.0-or-later
33

44
from logging import Logger
55
from pathlib import Path
66
from typing import Dict, List, Optional
77

8-
from lib4sbom.data.vulnerability import Vulnerability
9-
from lib4vex.generator import VEXGenerator
10-
118
from cve_bin_tool.log import LOGGER
129
from cve_bin_tool.util import CVEData, ProductInfo, Remarks
10+
from cve_bin_tool.vex_manager.handler import VexHandler
1311

1412

1513
class VEXGenerate:
@@ -103,40 +101,50 @@ def __init__(
103101
self.all_cve_data = all_cve_data
104102
self.sbom_serial_number = sbom_serial_number
105103

104+
# Initialize the VexHandler for generation operations
105+
self.vex_handler = VexHandler(self.logger)
106+
106107
def generate_vex(self) -> None:
107108
"""
108109
Generates a VEX (Vulnerability Exploitability eXchange) document based on the specified VEX type
109110
and stores it in the given filename.
110111
111-
This method sets up a VEX generator instance with the product name, release version, and other
112-
metadata. It automatically assigns a filename if none is provided, logs the update status if the
113-
file already exists, and generates the VEX document with product vulnerability data.
112+
This method delegates to the VexHandler for the actual generation, after preparing the data
113+
structure with product, vulnerabilities and metadata. It automatically assigns a filename if
114+
none is provided and logs update status if the file already exists.
114115
115116
Returns:
116117
None
117118
"""
118-
author = "Unknown Author"
119-
if self.vendor:
120-
author = self.vendor
121-
vexgen = VEXGenerator(vex_type=self.vextype, author=author)
122-
kwargs = {"name": self.product, "release": self.release}
123-
if self.sbom:
124-
kwargs["sbom"] = self.sbom
125-
vexgen.set_product(**kwargs)
126119
if not self.filename:
127-
self.logger.info(
120+
self.logger.debug(
128121
"No filename defined, generating a new filename with default naming convention."
129122
)
130123
self.filename = self.__generate_vex_filename()
124+
131125
if Path(self.filename).is_file():
132-
self.logger.info(f"Updating the VEX file: {self.filename}")
126+
self.logger.debug(f"Updating the VEX file: {self.filename}")
127+
128+
# Prepare data structure for VexHandler
129+
vex_data = {
130+
"product": {
131+
"name": self.product,
132+
"release": self.release,
133+
"vendor": self.vendor,
134+
},
135+
"project_name": self.product,
136+
"vulnerabilities": self.__get_vulnerabilities(),
137+
"metadata": self.__get_metadata(),
138+
}
139+
140+
# Add SBOM if available
141+
if self.sbom:
142+
vex_data["sbom"] = self.sbom
133143

134-
vexgen.generate(
135-
project_name=self.product,
136-
vex_data=self.__get_vulnerabilities(),
137-
metadata=self.__get_metadata(),
138-
filename=self.filename,
139-
)
144+
# Generate VEX document using the handler
145+
success = self.vex_handler.generate(vex_data, self.filename, self.vextype)
146+
if not success:
147+
self.logger.error(f"Failed to generate VEX file: {self.filename}")
140148

141149
def __generate_vex_filename(self) -> str:
142150
"""
@@ -183,56 +191,62 @@ def __get_metadata(self) -> Dict:
183191

184192
return metadata
185193

186-
def __get_vulnerabilities(self) -> List[Vulnerability]:
194+
def __get_vulnerabilities(self) -> List[Dict]:
187195
"""
188-
Retrieves and constructs a list of vulnerability objects based on the current CVE data.
196+
Prepares a list of vulnerability dictionaries for the VEX document based on the current CVE data.
189197
190198
This method iterates through all CVE data associated with the product and vendor,
191-
creating and configuring `Vulnerability` objects for each entry. It sets attributes
192-
like name, release, ID, description, status, and additional metadata such as package
193-
URLs (purl) and bill of materials (BOM) links. If a vulnerability includes comments
194-
or justification, these are added to the vulnerability details.
199+
creating vulnerability dictionaries for each entry with attributes like ID, description,
200+
status, and additional metadata such as package URLs (purl) and bill of materials (BOM) links.
195201
196202
Returns:
197-
List[Vulnerability]: A list of `Vulnerability` objects representing the identified
198-
vulnerabilities, enriched with metadata and details.
203+
List[Dict]: A list of vulnerability dictionaries ready for VexHandler to process.
199204
"""
200205
vulnerabilities = []
201206
for product_info, cve_data in self.all_cve_data.items():
202207
vendor, product, version, _, purl = product_info
203208
for cve in cve_data["cves"]:
204209
if isinstance(cve, str):
205210
continue
206-
vulnerability = Vulnerability(validation=self.vextype)
207-
vulnerability.initialise()
208-
vulnerability.set_name(product)
209-
vulnerability.set_release(version)
210-
vulnerability.set_id(cve.cve_number)
211-
vulnerability.set_description(cve.description)
212-
vulnerability.set_comment(cve.comments)
213-
vulnerability.set_status(self.analysis_state[self.vextype][cve.remarks])
211+
212+
# Create a vulnerability dictionary in the format expected by VexHandler
213+
vulnerability = {
214+
"name": product,
215+
"release": version,
216+
"id": cve.cve_number,
217+
"description": cve.description,
218+
"comment": cve.comments,
219+
"status": self.analysis_state[self.vextype][cve.remarks],
220+
}
221+
214222
if cve.justification:
215-
vulnerability.set_justification(cve.justification)
216-
if cve.response:
217-
vulnerability.set_value("remediation", cve.response[0])
223+
vulnerability["justification"] = cve.justification
224+
225+
if cve.response and len(cve.response) > 0:
226+
vulnerability["remediation"] = cve.response[0]
227+
218228
detail = (
219229
f"{cve.remarks.name}: {cve.comments}"
220230
if cve.comments
221231
else cve.remarks.name
222232
)
233+
223234
if purl is None:
224235
purl = f"pkg:generic/{vendor}/{product}@{version}"
236+
225237
bom_version = 1
226238
if self.sbom_serial_number != "":
227239
ref = f"urn:cdx:{self.sbom_serial_number}/{bom_version}#{purl}"
228240
else:
229241
ref = f"urn:cbt:{bom_version}/{vendor}#{product}:{version}"
230242

231-
vulnerability.set_value("purl", str(purl))
232-
vulnerability.set_value("bom_link", ref)
233-
vulnerability.set_value("action", detail)
234-
vulnerability.set_value("source", cve.data_source)
235-
vulnerability.set_value("updated", cve.last_modified)
236-
vulnerabilities.append(vulnerability.get_vulnerability())
243+
vulnerability["purl"] = str(purl)
244+
vulnerability["bom_link"] = ref
245+
vulnerability["action"] = detail
246+
vulnerability["source"] = cve.data_source
247+
vulnerability["updated"] = cve.last_modified
248+
249+
vulnerabilities.append(vulnerability)
250+
237251
self.logger.debug(f"Vulnerabilities: {vulnerabilities}")
238252
return vulnerabilities

cve_bin_tool/vex_manager/handler.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,27 @@
1010

1111
from cve_bin_tool.log import LOGGER
1212
from cve_bin_tool.util import ProductInfo, Remarks, decode_bom_ref, decode_purl
13-
from lib4vex import Validator as VEXValidator
13+
14+
# Import VEX validator with proper error handling for open source compatibility
15+
try:
16+
# Try the most common import path first
17+
from lib4vex import Validator as VEXValidator
18+
except ImportError:
19+
try:
20+
# Try alternative import paths
21+
from lib4vex.validator import VEXValidator
22+
except ImportError:
23+
try:
24+
from lib4vex.validate import Validator as VEXValidator
25+
except ImportError:
26+
# If no validator is available, create a clear error message
27+
VEXValidator = None
28+
VALIDATOR_IMPORT_ERROR = (
29+
"VEX validation functionality is not available. "
30+
"This may be due to an incompatible version of lib4vex. "
31+
"Please check that lib4vex is properly installed and up to date. "
32+
"Validation methods will be disabled but parsing and generation will still work."
33+
)
1434

1535
TriageData = Dict[str, Union[Dict[str, Any], Set[str]]]
1636

@@ -63,6 +83,10 @@ def __init__(self, logger=None):
6383
"""
6484
self.logger = logger or LOGGER.getChild(self.__class__.__name__)
6585

86+
# Warn user if validator is not available
87+
if VEXValidator is None:
88+
self.logger.warning(VALIDATOR_IMPORT_ERROR)
89+
6690
def parse(
6791
self, filename: str, vextype: str = "auto"
6892
) -> DefaultDict[ProductInfo, TriageData]:
@@ -88,7 +112,7 @@ def parse(
88112
if vextype == "auto":
89113
vextype = vexparser.get_type()
90114

91-
self.logger.info(f"Parsed VEX file: {filename} of type: {vextype}")
115+
self.logger.debug(f"Parsed VEX file: {filename} of type: {vextype}")
92116

93117
return self._process_parsed_data(vexparser, vextype)
94118

@@ -111,12 +135,20 @@ def validate(self, filename: str, vextype: str = "auto") -> bool:
111135
self.logger.error(f"VEX file not found: {filename}")
112136
return False
113137

138+
# Check if validator is available
139+
if VEXValidator is None:
140+
self.logger.error(
141+
"VEX validation is not available. "
142+
"Please ensure lib4vex is properly installed with validation support."
143+
)
144+
return False
145+
114146
try:
115147
validator = VEXValidator(vex_type=vextype)
116148
is_valid = validator.validate(filename)
117149

118150
if is_valid:
119-
self.logger.info(f"VEX file {filename} is valid")
151+
self.logger.debug(f"VEX file {filename} is valid")
120152
else:
121153
self.logger.error(f"VEX file {filename} is invalid")
122154

@@ -141,7 +173,7 @@ def generate(self, data: Dict, output_file: str, vextype: str) -> bool:
141173
try:
142174
generator = VEXGenerator(vex_type=vextype)
143175
generator.generate(data, output_file)
144-
self.logger.info(f"Generated {vextype} VEX file: {output_file}")
176+
self.logger.debug(f"Generated {vextype} VEX file: {output_file}")
145177
return True
146178

147179
except Exception as e:

0 commit comments

Comments
 (0)