From b7b2b918f65f276925a75efc0d6a4384b63210f7 Mon Sep 17 00:00:00 2001 From: SukhveerS <78963782+Rexbeast2@users.noreply.github.com> Date: Fri, 11 Aug 2023 00:37:38 +0530 Subject: [PATCH] feat: adding EPSS to console table (#3224) --- cve_bin_tool/output_engine/console.py | 12 ++++ test/test_output_engine.py | 91 ++++++++++++++++++++------- 2 files changed, 81 insertions(+), 22 deletions(-) diff --git a/cve_bin_tool/output_engine/console.py b/cve_bin_tool/output_engine/console.py index 0f43d90e83..e442c50d1c 100644 --- a/cve_bin_tool/output_engine/console.py +++ b/cve_bin_tool/output_engine/console.py @@ -104,6 +104,12 @@ def _output_console_nowrap( # group cve_data by its remarks and separately by paths for product_info, cve_data in all_cve_data.items(): for cve in cve_data["cves"]: + propability = "-" + percentile = "-" + for metric, field in cve.metric.items(): + if metric == "EPSS": + propability = str(round(field[0] * 100, 4)) + percentile = str(field[1]) cve_by_remarks[cve.remarks].append( { "vendor": product_info.vendor, @@ -114,6 +120,8 @@ def _output_console_nowrap( "severity": cve.severity, "score": cve.score, "cvss_version": cve.cvss_version, + "epss_propability": propability, + "epss_percentile": percentile, } ) path_elements = ", ".join(filter(None, cve_data["paths"])) @@ -149,6 +157,8 @@ def _output_console_nowrap( table.add_column("Source") table.add_column("Severity") table.add_column("Score (CVSS Version)") + table.add_column("EPSS propability") + table.add_column("EPSS percentile") if affected_versions != 0: table.add_column("Affected Versions") @@ -168,6 +178,8 @@ def _output_console_nowrap( Text.styled(cve_data["source"], color), Text.styled(cve_data["severity"], color), Text.styled(cvss_text, color), + Text.styled(cve_data["epss_propability"], color), + Text.styled(cve_data["epss_percentile"], color), ] if affected_versions != 0: cells.append(Text.styled(cve_data["affected_versions"], color)) diff --git a/test/test_output_engine.py b/test/test_output_engine.py index da4e334a54..b3964ccb26 100644 --- a/test/test_output_engine.py +++ b/test/test_output_engine.py @@ -103,6 +103,9 @@ class TestOutputEngine(unittest.TestCase): cvss_vector="C:H", data_source="NVD", last_modified="01-05-2019", + metric={ + "EPSS": [0.00126, "0.46387"], + }, ), CVE( "CVE-1234-1234", @@ -112,6 +115,9 @@ class TestOutputEngine(unittest.TestCase): cvss_vector="CVSS2.0/C:H", data_source="NVD", last_modified="11-11-2021", + metric={ + "EPSS": [0.01836, "0.79673"], + }, ), ], paths={""}, @@ -126,6 +132,9 @@ class TestOutputEngine(unittest.TestCase): cvss_vector="CVSS3.0/C:H/I:L/A:M", data_source="NVD", last_modified="12-12-2020", + metric={ + "EPSS": [0.03895, "0.37350"], + }, ) ], paths={""}, @@ -140,6 +149,9 @@ class TestOutputEngine(unittest.TestCase): cvss_vector="C:H/I:L/A:M", data_source="OSV", last_modified="20-10-2012", + metric={ + "EPSS": [0.0468, "0.34072"], + }, ) ], paths={""}, @@ -177,6 +189,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=3, cvss_vector="CVSS3.0/C:H/I:L/A:M", data_source="NVD", + metric={ + "EPSS": [0.0468, "0.34072"], + }, ) ], paths={""}, @@ -206,6 +221,7 @@ class TestOutputEngine(unittest.TestCase): cvss_version=0, cvss_vector="C:H", data_source="NVD", + metric={}, ), CVE( "CVE-9999-0001", @@ -214,6 +230,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="C:H", data_source="NVD", + metric={ + "EPSS": [0.299, "0.25934"], + }, ), CVE( "CVE-9999-0002", @@ -222,6 +241,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="C:H", data_source="NVD", + metric={ + "EPSS": [0.0285, "0.94667"], + }, ), CVE( "CVE-9999-0003", @@ -230,6 +252,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="C:H", data_source="NVD", + metric={ + "EPSS": [0.0468, "0.34072"], + }, ), CVE( "CVE-9999-0004", @@ -238,6 +263,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="C:H", data_source="NVD", + metric={ + "EPSS": [0.35337, "0.72282"], + }, ), CVE( "CVE-9999-0005", @@ -246,6 +274,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="C:H", data_source="NVD", + metric={ + "EPSS": [0.15370, "0.21433"], + }, ), CVE( "CVE-9999-0006", @@ -254,6 +285,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="C:H", data_source="NVD", + metric={ + "EPSS": [0.0513, "0.77186"], + }, ), CVE( "CVE-9999-0007", @@ -262,6 +296,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="C:H", data_source="NVD", + metric={ + "EPSS": [0.08360, "0.53389"], + }, ), CVE( "CVE-9999-0008", @@ -270,6 +307,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="C:H", data_source="NVD", + metric={ + "EPSS": [0.36957, "0.83771"], + }, ), CVE( "CVE-9999-9999", @@ -278,6 +318,9 @@ class TestOutputEngine(unittest.TestCase): cvss_version=2, cvss_vector="CVSS2.0/C:H", data_source="NVD", + metric={ + "EPSS": [0.012959, "0.67261"], + }, ), ], paths={""}, @@ -837,11 +880,11 @@ def test_output_console(self): ) expected_output = ( - "│ vendor0 │ product0 │ 1.0 │ CVE-1234-1234 │ NVD │ MEDIUM │ 4.2 (v2) │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-1234-1234 │ NVD │ LOW │ 1.2 (v2) │\n" - "│ vendor0 │ product0 │ 2.8.6 │ CVE-1234-1234 │ NVD │ LOW │ 2.5 (v3) │\n" - "│ vendor1 │ product1 │ 3.2.1.0 │ CVE-1234-1234 │ OSV │ HIGH │ 7.5 (v2) │\n" - "└─────────┴──────────┴─────────┴───────────────┴────────┴──────────┴──────────────────────┘\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-1234-1234 │ NVD │ MEDIUM │ 4.2 (v2) │ 0.126 │ 0.46387 │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-1234-1234 │ NVD │ LOW │ 1.2 (v2) │ 1.836 │ 0.79673 │\n" + "│ vendor0 │ product0 │ 2.8.6 │ CVE-1234-1234 │ NVD │ LOW │ 2.5 (v3) │ 3.895 │ 0.37350 │\n" + "│ vendor1 │ product1 │ 3.2.1.0 │ CVE-1234-1234 │ OSV │ HIGH │ 7.5 (v2) │ 4.68 │ 0.34072 │\n" + "└─────────┴──────────┴─────────┴───────────────┴────────┴──────────┴────────────────┴─────────────────┴────────────────┘\n" ) self.mock_file.seek(0) # reset file position @@ -870,21 +913,26 @@ def test_output_console_affected_versions(self): ) expected_output = ( - "│ vendor0 │ product0 │ 1.0 │ UNKNOWN │ NVD │ UNKNOWN │ 0 (v0) │ - │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0001 │ NVD │ MEDIUM │ 4.2 (v2) │ [0.9.0 - 1.2.0] │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0002 │ NVD │ MEDIUM │ 4.2 (v2) │ [0.9.0 - 1.2.0) │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0003 │ NVD │ MEDIUM │ 4.2 (v2) │ (0.9.0 - 1.2.0] │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0004 │ NVD │ MEDIUM │ 4.2 (v2) │ (0.9.0 - 1.2.0) │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0005 │ NVD │ MEDIUM │ 4.2 (v2) │ >= 0.9.0 │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0006 │ NVD │ MEDIUM │ 4.2 (v2) │ > 0.9.0 │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0007 │ NVD │ MEDIUM │ 4.2 (v2) │ <= 1.2.0 │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0008 │ NVD │ MEDIUM │ 4.2 (v2) │ < 1.2.0 │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-9999-9999 │ NVD │ LOW │ 1.2 (v2) │ - │\n" - "└─────────┴──────────┴─────────┴───────────────┴────────┴──────────┴──────────────────────┴───────────────────┘\n" + "│ vendor0 │ product0 │ 1.0 │ UNKNOWN │ NVD │ UNKNOWN │ 0 (v0) │ - │ - │ - │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0… │ NVD │ MEDIUM │ 4.2 (v2) │ 29.9 │ 0.25934 │ [0.9.0 - │\n" + "│ │ │ │ │ │ │ │ │ │ 1.2.0] │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0… │ NVD │ MEDIUM │ 4.2 (v2) │ 2.85 │ 0.94667 │ [0.9.0 - │\n" + "│ │ │ │ │ │ │ │ │ │ 1.2.0) │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0… │ NVD │ MEDIUM │ 4.2 (v2) │ 4.68 │ 0.34072 │ (0.9.0 - │\n" + "│ │ │ │ │ │ │ │ │ │ 1.2.0] │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0… │ NVD │ MEDIUM │ 4.2 (v2) │ 35.337 │ 0.72282 │ (0.9.0 - │\n" + "│ │ │ │ │ │ │ │ │ │ 1.2.0) │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0… │ NVD │ MEDIUM │ 4.2 (v2) │ 15.37 │ 0.21433 │ >= 0.9.0 │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0… │ NVD │ MEDIUM │ 4.2 (v2) │ 5.13 │ 0.77186 │ > 0.9.0 │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0… │ NVD │ MEDIUM │ 4.2 (v2) │ 8.36 │ 0.53389 │ <= 1.2.0 │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-0… │ NVD │ MEDIUM │ 4.2 (v2) │ 36.957 │ 0.83771 │ < 1.2.0 │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-9999-9… │ NVD │ LOW │ 1.2 (v2) │ 1.2959 │ 0.67261 │ - │\n" + "└─────────┴──────────┴─────────┴─────────────┴────────┴──────────┴─────────────┴────────────┴─────────────┴────────────┘\n" ) self.mock_file.seek(0) # reset file position result = self.mock_file.read() + print(result) self.assertIn(expected_output, result) def test_output_console_outfile(self): @@ -910,16 +958,15 @@ def test_output_console_outfile(self): ) expected_output = ( - "│ vendor0 │ product0 │ 1.0 │ CVE-1234-1234 │ NVD │ MEDIUM │ 4.2 (v2) │\n" - "│ vendor0 │ product0 │ 1.0 │ CVE-1234-1234 │ NVD │ LOW │ 1.2 (v2) │\n" - "│ vendor0 │ product0 │ 2.8.6 │ CVE-1234-1234 │ NVD │ LOW │ 2.5 (v3) │\n" - "│ vendor1 │ product1 │ 3.2.1.0 │ CVE-1234-1234 │ OSV │ HIGH │ 7.5 (v2) │\n" - "└─────────┴──────────┴─────────┴───────────────┴────────┴──────────┴──────────────────────┘\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-1234-1234 │ NVD │ MEDIUM │ 4.2 (v2) │ 0.126 │ 0.46387 │\n" + "│ vendor0 │ product0 │ 1.0 │ CVE-1234-1234 │ NVD │ LOW │ 1.2 (v2) │ 1.836 │ 0.79673 │\n" + "│ vendor0 │ product0 │ 2.8.6 │ CVE-1234-1234 │ NVD │ LOW │ 2.5 (v3) │ 3.895 │ 0.37350 │\n" + "│ vendor1 │ product1 │ 3.2.1.0 │ CVE-1234-1234 │ OSV │ HIGH │ 7.5 (v2) │ 4.68 │ 0.34072 │\n" + "└─────────┴──────────┴─────────┴───────────────┴────────┴──────────┴────────────────┴─────────────────┴────────────────┘\n" ) with open(tmpf.name, encoding="utf-8") as f: result = f.read() - self.assertIn(expected_output, result) Path(tmpf.name).unlink() # deleting tempfile