From 5c9acc3d9d59e724d97fea9b12abf5faad052775 Mon Sep 17 00:00:00 2001 From: Sergiy Kulanov Date: Sat, 21 Feb 2026 15:52:21 +0200 Subject: [PATCH] fix(trivy_operator): fix compliance severity logic and checkID comparison Fix two bugs in the Trivy Operator parser: 1. compliance_handler.py: The severity selection logic was inverted - it attempted to use check_severity when it was empty (causing KeyError) and fell back to result_severity when check_severity was present. Fixed to correctly prefer check-level severity, falling back to result-level severity when the check has no severity. 2. checks_handler.py: The checkID comparison used integer 0 instead of string "0", so `check_id != 0` was always True (string vs int). This caused bogus reference URLs and vulnerability IDs to be generated for checks without a checkID. Also changed from `check.get("checkID", "0")` to `check.get("checkID") or "0"` to handle explicit null values in JSON. Signed-off-by: Sergiy Kulanov --- dojo/tools/trivy_operator/checks_handler.py | 6 +- .../trivy_operator/compliance_handler.py | 5 +- .../trivy_operator/compliance_severity.json | 78 +++++++++++++++++++ .../configauditreport_missing_checkid.json | 48 ++++++++++++ unittests/tools/test_trivy_operator_parser.py | 23 +++++- 5 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 unittests/scans/trivy_operator/compliance_severity.json create mode 100644 unittests/scans/trivy_operator/configauditreport_missing_checkid.json diff --git a/dojo/tools/trivy_operator/checks_handler.py b/dojo/tools/trivy_operator/checks_handler.py index e9d27edcdb9..344dd2f8c9d 100644 --- a/dojo/tools/trivy_operator/checks_handler.py +++ b/dojo/tools/trivy_operator/checks_handler.py @@ -23,9 +23,9 @@ def handle_checks(self, labels, checks, test): for check in checks: check_title = check.get("title") check_severity = TRIVY_SEVERITIES[check.get("severity")] - check_id = check.get("checkID", "0") + check_id = check.get("checkID") or "0" check_references = "" - if check_id != 0: + if check_id != "0": check_references = ( "https://avd.aquasec.com/misconfig/kubernetes/" + check_id.lower() @@ -49,7 +49,7 @@ def handle_checks(self, labels, checks, test): ) if resource_namespace: finding.unsaved_tags = [resource_namespace] - if check_id: + if check_id != "0": finding.unsaved_vulnerability_ids = [UniformTrivyVulnID().return_uniformed_vulnid(check_id)] findings.append(finding) return findings diff --git a/dojo/tools/trivy_operator/compliance_handler.py b/dojo/tools/trivy_operator/compliance_handler.py index a73c60c4dcd..d529e2b0f57 100644 --- a/dojo/tools/trivy_operator/compliance_handler.py +++ b/dojo/tools/trivy_operator/compliance_handler.py @@ -31,10 +31,7 @@ def handle_compliance(self, benchmarkreport, test): check_severity = check.get("severity", "") check_target = check.get("target", "") check_title = check.get("title", "") - if not check_severity: - severity = TRIVY_SEVERITIES[check_severity] - else: - severity = TRIVY_SEVERITIES[result_severity] + severity = TRIVY_SEVERITIES[check_severity] if check_severity else TRIVY_SEVERITIES[result_severity] description += "**result description:** " + result_description + "\n" description += "**result id:** " + result_id + "\n" description += "**result name:** " + result_name + "\n" diff --git a/unittests/scans/trivy_operator/compliance_severity.json b/unittests/scans/trivy_operator/compliance_severity.json new file mode 100644 index 00000000000..20f401af91d --- /dev/null +++ b/unittests/scans/trivy_operator/compliance_severity.json @@ -0,0 +1,78 @@ +{ + "apiVersion": "aquasecurity.github.io/v1alpha1", + "kind": "ClusterComplianceReport", + "metadata": { + "creationTimestamp": "2024-03-05T10:38:15Z", + "generation": 1, + "labels": { + "app.kubernetes.io/instance": "trivy-operator", + "app.kubernetes.io/managed-by": "kubectl", + "app.kubernetes.io/name": "trivy-operator", + "app.kubernetes.io/version": "0.18.5" + }, + "name": "cis", + "resourceVersion": "1649372", + "uid": "test-compliance-severity" + }, + "spec": { + "compliance": { + "controls": [], + "description": "Test Compliance Severity", + "id": "test", + "title": "Test Compliance Severity Report", + "version": "1.0" + }, + "cron": "0 */6 * * *", + "reportType": "all" + }, + "status": { + "detailReport": { + "description": "Test report for compliance severity logic", + "id": "test", + "relatedResources": [], + "results": [ + { + "checks": [ + { + "category": "Kubernetes Security Check", + "checkID": "AVD-KSV-0001", + "description": "Check with its own severity", + "messages": [ + "Test message 1" + ], + "remediation": "Fix it", + "severity": "MEDIUM", + "success": false, + "target": "/test-target-1", + "title": "Check with severity" + }, + { + "category": "Kubernetes Security Check", + "checkID": "AVD-KSV-0002", + "description": "Check without severity", + "messages": [ + "Test message 2" + ], + "remediation": "Fix it too", + "severity": "", + "success": false, + "target": "/test-target-2", + "title": "Check without severity" + } + ], + "description": "Test result", + "id": "1.1.1", + "name": "Test result name", + "severity": "HIGH" + } + ], + "title": "Test Compliance Severity Report", + "version": "1.0" + }, + "summary": { + "failCount": 2, + "passCount": 0 + }, + "updateTimestamp": "2024-03-05T10:38:15Z" + } +} diff --git a/unittests/scans/trivy_operator/configauditreport_missing_checkid.json b/unittests/scans/trivy_operator/configauditreport_missing_checkid.json new file mode 100644 index 00000000000..67066415b20 --- /dev/null +++ b/unittests/scans/trivy_operator/configauditreport_missing_checkid.json @@ -0,0 +1,48 @@ +{ + "apiVersion": "aquasecurity.github.io/v1alpha1", + "kind": "ConfigAuditReport", + "metadata": { + "annotations": { + "trivy-operator.aquasecurity.github.io/report-ttl": "24h0m0s" + }, + "creationTimestamp": "2023-03-23T16:22:54Z", + "generation": 1, + "labels": { + "plugin-config-hash": "659b7b9c46", + "resource-spec-hash": "fc85b485f", + "trivy-operator.resource.kind": "ReplicaSet", + "trivy-operator.resource.name": "test-deployment-12345", + "trivy-operator.resource.namespace": "default" + }, + "name": "replicaset-test-deployment-12345", + "namespace": "default", + "resourceVersion": "1268", + "uid": "test-missing-checkid" + }, + "report": { + "checks": [ + { + "category": "Kubernetes Security Check", + "description": "A check without a checkID field", + "messages": [ + "Container 'test' of ReplicaSet 'test-deployment-12345' has an issue" + ], + "severity": "MEDIUM", + "success": false, + "title": "Missing checkID test" + } + ], + "scanner": { + "name": "Trivy", + "vendor": "Aqua Security", + "version": "dev" + }, + "summary": { + "criticalCount": 0, + "highCount": 0, + "lowCount": 0, + "mediumCount": 1 + }, + "updateTimestamp": "2023-03-23T16:22:54Z" + } +} diff --git a/unittests/tools/test_trivy_operator_parser.py b/unittests/tools/test_trivy_operator_parser.py index 2eaa0b6ac42..d715b696730 100644 --- a/unittests/tools/test_trivy_operator_parser.py +++ b/unittests/tools/test_trivy_operator_parser.py @@ -138,7 +138,7 @@ def test_cis_benchmark(self): self.assertEqual(len(findings), 795) finding = findings[0] self.assertEqual("5.1.2 AVD-KSV-0041 /clusterrole-admin", finding.title) - self.assertEqual("High", finding.severity) + self.assertEqual("Critical", finding.severity) self.assertEqual(1, len(finding.unsaved_vulnerability_ids)) self.assertEqual("AVD-KSV-0041", finding.unsaved_vulnerability_ids[0]) finding = findings[40] @@ -169,3 +169,24 @@ def test_findings_clustercompliancereport(self): parser = TrivyOperatorParser() findings = parser.get_findings(test_file, Test()) self.assertEqual(len(findings), 2) + + def test_compliance_severity_logic(self): + with sample_path("compliance_severity.json").open(encoding="utf-8") as test_file: + parser = TrivyOperatorParser() + findings = parser.get_findings(test_file, Test()) + self.assertEqual(len(findings), 2) + # First check has severity MEDIUM, result has severity HIGH -> uses check's MEDIUM + self.assertEqual("Medium", findings[0].severity) + # Second check has empty severity, result has severity HIGH -> falls back to HIGH + self.assertEqual("High", findings[1].severity) + + def test_configauditreport_missing_checkid(self): + with sample_path("configauditreport_missing_checkid.json").open(encoding="utf-8") as test_file: + parser = TrivyOperatorParser() + findings = parser.get_findings(test_file, Test()) + self.assertEqual(len(findings), 1) + finding = findings[0] + self.assertEqual("Medium", finding.severity) + self.assertEqual("0 - Missing checkID test", finding.title) + # When checkID is "0", references should be empty (not a bogus URL) + self.assertEqual("", finding.references)