Skip to content
Merged
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
2 changes: 1 addition & 1 deletion cve_bin_tool/vex_manager/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def get_vulnerabilities(self) -> List[Vulnerability]:
if purl is None:
purl = f"pkg:generic/{vendor}/{product}@{version}"

vulnerability.set_value("purl", purl)
vulnerability.set_value("purl", str(purl))
vulnerability.set_value("bom_link", ref)
vulnerability.set_value("action", detail)
vulnerability.set_value("source", cve.data_source)
Expand Down
144 changes: 144 additions & 0 deletions test/test_vex.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import unittest
from pathlib import Path

import pytest

from cve_bin_tool.util import CVE, CVEData, ProductInfo, Remarks
from cve_bin_tool.vex_manager.generate import VEXGenerate
from cve_bin_tool.vex_manager.parse import VEXParse

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

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

def test_output_openvex(self):
"""Test VEX output generation"""

vexgen = VEXGenerate(
"dummy-product",
"1.0",
"dummy-vendor",
"generated_openvex_vex.json",
"openvex",
self.FORMATTED_DATA,
)
vexgen.generate_vex()

with open("generated_openvex_vex.json") as f:
json_data = json.load(f)
# remove dynamic fields such as timestamp and id
json_data.pop("@id", None)
json_data.pop("timestamp", None)
for statement in json_data.get("statements", []):
statement.pop("timestamp", None)
statement.pop("action_statement_timestamp", None)

with open(str(VEX_PATH / "test_openvex_vex.json")) as f:
expected_json = json.load(f)
# remove dynamic fields such as timestamp and id
expected_json.pop("@id", None)
expected_json.pop("timestamp", None)
for statement in expected_json.get("statements", []):
statement.pop("timestamp", None)
statement.pop("action_statement_timestamp", None)

assert json_data == expected_json

Path("generated_openvex_vex.json").unlink()


class TestVexParse:
PARSED_DATA_WITH_PURL = {
ProductInfo(
vendor="vendor0",
product="product0",
version="1.0",
location="location/to/product",
purl="pkg:generic/vendor0/[email protected]",
): {
"CVE-1234-1004": {
"remarks": Remarks.NewFound,
"comments": "",
"response": [],
},
"CVE-1234-1005": {
"remarks": Remarks.NotAffected,
"comments": "",
"response": [],
},
"paths": {},
},
ProductInfo(
vendor="vendor0",
product="product0",
version="2.8.6",
location="location/to/product",
purl="pkg:generic/vendor0/[email protected]",
): {
"CVE-1234-1007": {
"remarks": Remarks.Mitigated,
"comments": "",
"response": [],
},
"CVE-1234-1008": {
"remarks": Remarks.NewFound,
"comments": "",
"response": [],
},
"paths": {},
},
}
PARSED_DATA_WITHOUT_PURL = {
ProductInfo(
vendor="vendor0",
product="product0",
version="1.0",
location="location/to/product",
): {
"CVE-1234-1004": {
"remarks": Remarks.NewFound,
"comments": "",
"response": [],
},
"CVE-1234-1005": {
"remarks": Remarks.NotAffected,
"comments": "",
"response": [],
"justification": "code_not_reachable",
},
"paths": {},
},
ProductInfo(
vendor="vendor0",
product="product0",
version="2.8.6",
location="location/to/product",
): {
"CVE-1234-1007": {
"remarks": Remarks.Mitigated,
"comments": "",
"response": [],
},
"CVE-1234-1008": {
"remarks": Remarks.NewFound,
"comments": "",
"response": [],
},
"paths": {},
},
}

@pytest.mark.parametrize(
"vex_format, vex_filename, expected_parsed_data",
[
("cyclonedx", "test_cyclonedx_vex.json", PARSED_DATA_WITHOUT_PURL),
],
)
def test_parse_cyclonedx(self, vex_format, vex_filename, expected_parsed_data):
"""Test parsing of CycloneDX VEX"""
vexparse = VEXParse(str(VEX_PATH / vex_filename), vex_format)
parsed_data = vexparse.parse_vex()
assert parsed_data == expected_parsed_data

@pytest.mark.parametrize(
"vex_format, vex_filename, expected_parsed_data",
[
("openvex", "test_openvex_vex.json", PARSED_DATA_WITH_PURL),
],
)
def test_parse_openvex(self, vex_format, vex_filename, expected_parsed_data):
"""Test parsing of OpenVEX VEX"""
vexparse = VEXParse(str(VEX_PATH / vex_filename), vex_format)
parsed_data = vexparse.parse_vex()
assert parsed_data == expected_parsed_data


if __name__ == "__main__":
unittest.main()
71 changes: 71 additions & 0 deletions test/vex/test_openvex_vex.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://openvex.dev/docs/public/vex-cfa9bc2b35954876b90f9a1487e8bf83",
"author": "dummy-vendor",
"role": "Document Creator",
"timestamp": "2024-07-05T21:20:34",
"version": "1",
"statements": [
{
"vulnerability": {
"@id": "https://nvd.nist.gov/vuln/detail/CVE-1234-1004",
"name": "CVE-1234-1004"
},
"timestamp": "2024-07-05T21:20:34",
"products": [
{
"@id": "pkg:generic/vendor0/[email protected]"
}
],
"status": "under_investigation",
"action_statement": "",
"action_statement_timestamp": "2024-07-05T21:20:34"
},
{
"vulnerability": {
"@id": "https://nvd.nist.gov/vuln/detail/CVE-1234-1005",
"name": "CVE-1234-1005"
},
"timestamp": "2024-07-05T21:20:34",
"products": [
{
"@id": "pkg:generic/vendor0/[email protected]"
}
],
"status": "not_affected",
"justification": null,
"action_statement": "Detail field populated.",
"action_statement_timestamp": "2024-07-05T21:20:34"
},
{
"vulnerability": {
"@id": "https://nvd.nist.gov/vuln/detail/CVE-1234-1007",
"name": "CVE-1234-1007"
},
"timestamp": "2024-07-05T21:20:34",
"products": [
{
"@id": "pkg:generic/vendor0/[email protected]"
}
],
"status": "fixed",
"action_statement": "Data field populated.",
"action_statement_timestamp": "2024-07-05T21:20:34"
},
{
"vulnerability": {
"@id": "https://nvd.nist.gov/vuln/detail/CVE-1234-1008",
"name": "CVE-1234-1008"
},
"timestamp": "2024-07-05T21:20:34",
"products": [
{
"@id": "pkg:generic/vendor0/[email protected]"
}
],
"status": "under_investigation",
"action_statement": "",
"action_statement_timestamp": "2024-07-05T21:20:34"
}
]
}