Skip to content

Commit c3dfa4b

Browse files
authored
test: openvex parse and generation test (#4244)
* test: openvex parse and generation test * fix: seperate tests
1 parent 81dac56 commit c3dfa4b

File tree

3 files changed

+216
-1
lines changed

3 files changed

+216
-1
lines changed

cve_bin_tool/vex_manager/generate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def get_vulnerabilities(self) -> List[Vulnerability]:
146146
if purl is None:
147147
purl = f"pkg:generic/{vendor}/{product}@{version}"
148148

149-
vulnerability.set_value("purl", purl)
149+
vulnerability.set_value("purl", str(purl))
150150
vulnerability.set_value("bom_link", ref)
151151
vulnerability.set_value("action", detail)
152152
vulnerability.set_value("source", cve.data_source)

test/test_vex.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import unittest
55
from pathlib import Path
66

7+
import pytest
8+
79
from cve_bin_tool.util import CVE, CVEData, ProductInfo, Remarks
810
from cve_bin_tool.vex_manager.generate import VEXGenerate
11+
from cve_bin_tool.vex_manager.parse import VEXParse
912

1013
TEST_DIR = Path(__file__).parent.resolve()
1114
VEX_PATH = TEST_DIR / "vex"
@@ -113,6 +116,147 @@ def test_output_cyclonedx(self):
113116

114117
Path("generated_cyclonedx_vex.json").unlink()
115118

119+
def test_output_openvex(self):
120+
"""Test VEX output generation"""
121+
122+
vexgen = VEXGenerate(
123+
"dummy-product",
124+
"1.0",
125+
"dummy-vendor",
126+
"generated_openvex_vex.json",
127+
"openvex",
128+
self.FORMATTED_DATA,
129+
)
130+
vexgen.generate_vex()
131+
132+
with open("generated_openvex_vex.json") as f:
133+
json_data = json.load(f)
134+
# remove dynamic fields such as timestamp and id
135+
json_data.pop("@id", None)
136+
json_data.pop("timestamp", None)
137+
for statement in json_data.get("statements", []):
138+
statement.pop("timestamp", None)
139+
statement.pop("action_statement_timestamp", None)
140+
141+
with open(str(VEX_PATH / "test_openvex_vex.json")) as f:
142+
expected_json = json.load(f)
143+
# remove dynamic fields such as timestamp and id
144+
expected_json.pop("@id", None)
145+
expected_json.pop("timestamp", None)
146+
for statement in expected_json.get("statements", []):
147+
statement.pop("timestamp", None)
148+
statement.pop("action_statement_timestamp", None)
149+
150+
assert json_data == expected_json
151+
152+
Path("generated_openvex_vex.json").unlink()
153+
154+
155+
class TestVexParse:
156+
PARSED_DATA_WITH_PURL = {
157+
ProductInfo(
158+
vendor="vendor0",
159+
product="product0",
160+
version="1.0",
161+
location="location/to/product",
162+
purl="pkg:generic/vendor0/[email protected]",
163+
): {
164+
"CVE-1234-1004": {
165+
"remarks": Remarks.NewFound,
166+
"comments": "",
167+
"response": [],
168+
},
169+
"CVE-1234-1005": {
170+
"remarks": Remarks.NotAffected,
171+
"comments": "",
172+
"response": [],
173+
},
174+
"paths": {},
175+
},
176+
ProductInfo(
177+
vendor="vendor0",
178+
product="product0",
179+
version="2.8.6",
180+
location="location/to/product",
181+
purl="pkg:generic/vendor0/[email protected]",
182+
): {
183+
"CVE-1234-1007": {
184+
"remarks": Remarks.Mitigated,
185+
"comments": "",
186+
"response": [],
187+
},
188+
"CVE-1234-1008": {
189+
"remarks": Remarks.NewFound,
190+
"comments": "",
191+
"response": [],
192+
},
193+
"paths": {},
194+
},
195+
}
196+
PARSED_DATA_WITHOUT_PURL = {
197+
ProductInfo(
198+
vendor="vendor0",
199+
product="product0",
200+
version="1.0",
201+
location="location/to/product",
202+
): {
203+
"CVE-1234-1004": {
204+
"remarks": Remarks.NewFound,
205+
"comments": "",
206+
"response": [],
207+
},
208+
"CVE-1234-1005": {
209+
"remarks": Remarks.NotAffected,
210+
"comments": "",
211+
"response": [],
212+
"justification": "code_not_reachable",
213+
},
214+
"paths": {},
215+
},
216+
ProductInfo(
217+
vendor="vendor0",
218+
product="product0",
219+
version="2.8.6",
220+
location="location/to/product",
221+
): {
222+
"CVE-1234-1007": {
223+
"remarks": Remarks.Mitigated,
224+
"comments": "",
225+
"response": [],
226+
},
227+
"CVE-1234-1008": {
228+
"remarks": Remarks.NewFound,
229+
"comments": "",
230+
"response": [],
231+
},
232+
"paths": {},
233+
},
234+
}
235+
236+
@pytest.mark.parametrize(
237+
"vex_format, vex_filename, expected_parsed_data",
238+
[
239+
("cyclonedx", "test_cyclonedx_vex.json", PARSED_DATA_WITHOUT_PURL),
240+
],
241+
)
242+
def test_parse_cyclonedx(self, vex_format, vex_filename, expected_parsed_data):
243+
"""Test parsing of CycloneDX VEX"""
244+
vexparse = VEXParse(str(VEX_PATH / vex_filename), vex_format)
245+
parsed_data = vexparse.parse_vex()
246+
assert parsed_data == expected_parsed_data
247+
248+
@pytest.mark.parametrize(
249+
"vex_format, vex_filename, expected_parsed_data",
250+
[
251+
("openvex", "test_openvex_vex.json", PARSED_DATA_WITH_PURL),
252+
],
253+
)
254+
def test_parse_openvex(self, vex_format, vex_filename, expected_parsed_data):
255+
"""Test parsing of OpenVEX VEX"""
256+
vexparse = VEXParse(str(VEX_PATH / vex_filename), vex_format)
257+
parsed_data = vexparse.parse_vex()
258+
assert parsed_data == expected_parsed_data
259+
116260

117261
if __name__ == "__main__":
118262
unittest.main()

test/vex/test_openvex_vex.json

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"@context": "https://openvex.dev/ns/v0.2.0",
3+
"@id": "https://openvex.dev/docs/public/vex-cfa9bc2b35954876b90f9a1487e8bf83",
4+
"author": "dummy-vendor",
5+
"role": "Document Creator",
6+
"timestamp": "2024-07-05T21:20:34",
7+
"version": "1",
8+
"statements": [
9+
{
10+
"vulnerability": {
11+
"@id": "https://nvd.nist.gov/vuln/detail/CVE-1234-1004",
12+
"name": "CVE-1234-1004"
13+
},
14+
"timestamp": "2024-07-05T21:20:34",
15+
"products": [
16+
{
17+
"@id": "pkg:generic/vendor0/[email protected]"
18+
}
19+
],
20+
"status": "under_investigation",
21+
"action_statement": "",
22+
"action_statement_timestamp": "2024-07-05T21:20:34"
23+
},
24+
{
25+
"vulnerability": {
26+
"@id": "https://nvd.nist.gov/vuln/detail/CVE-1234-1005",
27+
"name": "CVE-1234-1005"
28+
},
29+
"timestamp": "2024-07-05T21:20:34",
30+
"products": [
31+
{
32+
"@id": "pkg:generic/vendor0/[email protected]"
33+
}
34+
],
35+
"status": "not_affected",
36+
"justification": null,
37+
"action_statement": "Detail field populated.",
38+
"action_statement_timestamp": "2024-07-05T21:20:34"
39+
},
40+
{
41+
"vulnerability": {
42+
"@id": "https://nvd.nist.gov/vuln/detail/CVE-1234-1007",
43+
"name": "CVE-1234-1007"
44+
},
45+
"timestamp": "2024-07-05T21:20:34",
46+
"products": [
47+
{
48+
"@id": "pkg:generic/vendor0/[email protected]"
49+
}
50+
],
51+
"status": "fixed",
52+
"action_statement": "Data field populated.",
53+
"action_statement_timestamp": "2024-07-05T21:20:34"
54+
},
55+
{
56+
"vulnerability": {
57+
"@id": "https://nvd.nist.gov/vuln/detail/CVE-1234-1008",
58+
"name": "CVE-1234-1008"
59+
},
60+
"timestamp": "2024-07-05T21:20:34",
61+
"products": [
62+
{
63+
"@id": "pkg:generic/vendor0/[email protected]"
64+
}
65+
],
66+
"status": "under_investigation",
67+
"action_statement": "",
68+
"action_statement_timestamp": "2024-07-05T21:20:34"
69+
}
70+
]
71+
}

0 commit comments

Comments
 (0)