diff --git a/dojo/tools/trivy_operator/checks_handler.py b/dojo/tools/trivy_operator/checks_handler.py index 506058ef6e..79876bef28 100644 --- a/dojo/tools/trivy_operator/checks_handler.py +++ b/dojo/tools/trivy_operator/checks_handler.py @@ -17,7 +17,9 @@ def handle_checks(self, labels, checks, test): resource_kind = labels.get("trivy-operator.resource.kind", "") resource_name = labels.get("trivy-operator.resource.name", "") container_name = labels.get("trivy-operator.container.name", "") - service = f"{resource_namespace}/{resource_kind}/{resource_name}" + # resource_name changes every deploy (ReplicaSet-generated) — excluded so + # service stays stable and close_old_findings matches across redeploys. + service = f"{resource_namespace}/{resource_kind}" if container_name: service = f"{service}/{container_name}" for check in checks: diff --git a/dojo/tools/trivy_operator/secrets_handler.py b/dojo/tools/trivy_operator/secrets_handler.py index 1bead526d7..150c2c04dd 100644 --- a/dojo/tools/trivy_operator/secrets_handler.py +++ b/dojo/tools/trivy_operator/secrets_handler.py @@ -24,7 +24,9 @@ def handle_secrets(self, labels, secrets, test, image=None): resource_kind = labels.get("trivy-operator.resource.kind", "") resource_name = labels.get("trivy-operator.resource.name", "") container_name = labels.get("trivy-operator.container.name", "") - service = f"{resource_namespace}/{resource_kind}/{resource_name}" + # resource_name changes every deploy (ReplicaSet-generated) — excluded so + # service stays stable and close_old_findings matches across redeploys. + service = f"{resource_namespace}/{resource_kind}" if container_name: service = f"{service}/{container_name}" for secret in secrets: diff --git a/dojo/tools/trivy_operator/vulnerability_handler.py b/dojo/tools/trivy_operator/vulnerability_handler.py index 64826292b6..7aac4d4856 100644 --- a/dojo/tools/trivy_operator/vulnerability_handler.py +++ b/dojo/tools/trivy_operator/vulnerability_handler.py @@ -24,7 +24,9 @@ def handle_vulns(self, labels, vulnerabilities, test, image=None): resource_kind = labels.get("trivy-operator.resource.kind", "") resource_name = labels.get("trivy-operator.resource.name", "") container_name = labels.get("trivy-operator.container.name", "") - service = f"{resource_namespace}/{resource_kind}/{resource_name}" + # resource_name changes every deploy (ReplicaSet-generated) — excluded so + # service stays stable and close_old_findings matches across redeploys. + service = f"{resource_namespace}/{resource_kind}" if container_name: service = f"{service}/{container_name}" for vulnerability in vulnerabilities: diff --git a/unittests/tools/test_trivy_operator_parser.py b/unittests/tools/test_trivy_operator_parser.py index f9ccaf16c4..f88795ce4c 100644 --- a/unittests/tools/test_trivy_operator_parser.py +++ b/unittests/tools/test_trivy_operator_parser.py @@ -64,6 +64,19 @@ def test_vulnerabilityreport_single_vulns(self): self.assertEqual(4.2, finding.cvssv3_score) self.assertEqual(True, finding.fix_available) + def test_vulnerabilityreport_service_excludes_volatile_replicaset_name(self): + # regression test: `service` must stay stable across redeploys, otherwise + # DefectDojo's close_old_findings can never match findings from a previous + # deploy generation and old findings are never closed (see issue #16026). + # The fixture's `trivy-operator.resource.name` label is a ReplicaSet-generated + # name ("nginx-deployment-965685897") and must NOT end up in `service`. + with sample_path("vulnerabilityreport_single_vuln.json").open(encoding="utf-8") as test_file: + parser = TrivyOperatorParser() + findings = parser.get_findings(test_file, Test()) + finding = findings[0] + self.assertEqual("default/ReplicaSet/nginx", finding.service) + self.assertNotIn("nginx-deployment-965685897", finding.service) + def test_vulnerabilityreport_many(self): with sample_path("vulnerabilityreport_many.json").open(encoding="utf-8") as test_file: parser = TrivyOperatorParser()