From 00fd7449c69320f9f654d3d79553d273f40d9c87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20H=C3=A4kli?= Date: Fri, 9 May 2025 12:28:58 +0300 Subject: [PATCH 1/2] Add test for VulnerabilityAnalysis sorting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Riku Häkli --- tests/test_model_vulnerability.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test_model_vulnerability.py b/tests/test_model_vulnerability.py index 830cbba2..74af5a25 100644 --- a/tests/test_model_vulnerability.py +++ b/tests/test_model_vulnerability.py @@ -20,12 +20,18 @@ from unittest import TestCase from cyclonedx.model import XsUri -from cyclonedx.model.impact_analysis import ImpactAnalysisAffectedStatus +from cyclonedx.model.impact_analysis import ( + ImpactAnalysisAffectedStatus, + ImpactAnalysisJustification, + ImpactAnalysisResponse, + ImpactAnalysisState, +) from cyclonedx.model.vulnerability import ( BomTarget, BomTargetVersionRange, Vulnerability, VulnerabilityAdvisory, + VulnerabilityAnalysis, VulnerabilityRating, VulnerabilityReference, VulnerabilityScoreSource, @@ -334,3 +340,24 @@ def test_sort(self) -> None: sorted_targets = sorted(targets) expected_targets = reorder(targets, expected_order) self.assertListEqual(sorted_targets, expected_targets) + + +class TestModelVulnerabilityAnalysis(TestCase): + + def test_sort(self) -> None: + # expected sort order: ([state], [justification], [responses], [detail], [first_issued], [last_updated]) + expected_order = [3, 1, 0, 2, 5, 4] + analyses = [ + VulnerabilityAnalysis(state=ImpactAnalysisState.EXPLOITABLE), + VulnerabilityAnalysis(state=ImpactAnalysisState.EXPLOITABLE, + responses=[ImpactAnalysisResponse.CAN_NOT_FIX]), + VulnerabilityAnalysis(state=ImpactAnalysisState.NOT_AFFECTED, + justification=ImpactAnalysisJustification.CODE_NOT_PRESENT), + VulnerabilityAnalysis(state=ImpactAnalysisState.EXPLOITABLE, + justification=ImpactAnalysisJustification.REQUIRES_ENVIRONMENT), + VulnerabilityAnalysis(first_issued=datetime(2024, 4, 4), last_updated=datetime(2025, 5, 5)), + VulnerabilityAnalysis(first_issued=datetime(2023, 3, 3), last_updated=datetime(2023, 3, 3)), + ] + sorted_analyses = sorted(analyses) + expected_analyses = reorder(analyses, expected_order) + self.assertListEqual(sorted_analyses, expected_analyses) From ce25586ce66c20c9443e51100b25a0253f0d94e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20H=C3=A4kli?= Date: Fri, 9 May 2025 12:29:06 +0300 Subject: [PATCH 2/2] Add missing VulnerabilityAnalysis comparator for sorting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Riku Häkli --- cyclonedx/model/vulnerability.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cyclonedx/model/vulnerability.py b/cyclonedx/model/vulnerability.py index d4c17d4e..9893ef32 100644 --- a/cyclonedx/model/vulnerability.py +++ b/cyclonedx/model/vulnerability.py @@ -349,6 +349,11 @@ def __eq__(self, other: object) -> bool: return self.__comparable_tuple() == other.__comparable_tuple() return False + def __lt__(self, other: Any) -> bool: + if isinstance(other, VulnerabilityAnalysis): + return self.__comparable_tuple() < other.__comparable_tuple() + return NotImplemented + def __hash__(self) -> int: return hash(self.__comparable_tuple())