From 95e8c4531cb7c6eff7542be7af3c4cc5c2802a14 Mon Sep 17 00:00:00 2001 From: "sanskarsharma3110@gmail.com" Date: Fri, 5 Jul 2024 23:24:12 +0530 Subject: [PATCH 1/2] test: openvex parse and generation test --- cve_bin_tool/vex_manager/generate.py | 2 +- test/test_vex.py | 133 +++++++++++++++++++++++++++ test/vex/test_openvex_vex.json | 71 ++++++++++++++ 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 test/vex/test_openvex_vex.json diff --git a/cve_bin_tool/vex_manager/generate.py b/cve_bin_tool/vex_manager/generate.py index a899ceffc6..ee5fda831f 100644 --- a/cve_bin_tool/vex_manager/generate.py +++ b/cve_bin_tool/vex_manager/generate.py @@ -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) diff --git a/test/test_vex.py b/test/test_vex.py index 120c62ae69..0cc4faf87b 100644 --- a/test/test_vex.py +++ b/test/test_vex.py @@ -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" @@ -113,6 +116,136 @@ 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/product0@1.0", + ): { + "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/product0@2.8.6", + ): { + "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), + ("openvex", "test_openvex_vex.json", PARSED_DATA_WITH_PURL), + ], + ) + def test_parse_vex(self, vex_format, vex_filename, expected_parsed_data): + """Test VEX parsing""" + 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() diff --git a/test/vex/test_openvex_vex.json b/test/vex/test_openvex_vex.json new file mode 100644 index 0000000000..5b0ac96ba8 --- /dev/null +++ b/test/vex/test_openvex_vex.json @@ -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/product0@1.0" + } + ], + "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/product0@1.0" + } + ], + "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/product0@2.8.6" + } + ], + "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/product0@2.8.6" + } + ], + "status": "under_investigation", + "action_statement": "", + "action_statement_timestamp": "2024-07-05T21:20:34" + } + ] +} From c50001c3930a86d5e9b6672c20a3b53cfd4f8e62 Mon Sep 17 00:00:00 2001 From: "sanskarsharma3110@gmail.com" Date: Tue, 9 Jul 2024 00:55:57 +0530 Subject: [PATCH 2/2] fix: seperate tests --- test/test_vex.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/test_vex.py b/test/test_vex.py index 0cc4faf87b..e33fa5ffb2 100644 --- a/test/test_vex.py +++ b/test/test_vex.py @@ -237,11 +237,22 @@ class TestVexParse: "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_vex(self, vex_format, vex_filename, expected_parsed_data): - """Test VEX parsing""" + 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