diff --git a/dojo/tools/trivy_operator/checks_handler.py b/dojo/tools/trivy_operator/checks_handler.py index e9d27edcdb9..86b3952c1ca 100644 --- a/dojo/tools/trivy_operator/checks_handler.py +++ b/dojo/tools/trivy_operator/checks_handler.py @@ -30,16 +30,27 @@ def handle_checks(self, labels, checks, test): "https://avd.aquasec.com/misconfig/kubernetes/" + check_id.lower() ) + check_remediation = check.get("remediation", "") check_description = check.get("description", "") + check_messages = check.get("messages", []) + check_category = check.get("category", "") check_description += "\n**container.name:** " + container_name check_description += "\n**resource.kind:** " + resource_kind check_description += "\n**resource.name:** " + resource_name check_description += "\n**resource.namespace:** " + resource_namespace + mitigation = check_remediation or None + if check_messages: + messages_text = "\n".join(check_messages) + if mitigation: + mitigation += "\n\n" + messages_text + else: + mitigation = messages_text title = f"{check_id} - {check_title}" finding = Finding( test=test, title=title, severity=check_severity, + mitigation=mitigation, references=check_references, description=check_description, static_finding=True, @@ -47,8 +58,8 @@ def handle_checks(self, labels, checks, test): service=service, fix_available=True, ) - if resource_namespace: - finding.unsaved_tags = [resource_namespace] + finding_tags = [resource_namespace, check_category] + finding.unsaved_tags = [tag for tag in finding_tags if tag] if check_id: finding.unsaved_vulnerability_ids = [UniformTrivyVulnID().return_uniformed_vulnid(check_id)] findings.append(finding) diff --git a/dojo/tools/trivy_operator/vulnerability_handler.py b/dojo/tools/trivy_operator/vulnerability_handler.py index 685f0760763..32ed7baafad 100644 --- a/dojo/tools/trivy_operator/vulnerability_handler.py +++ b/dojo/tools/trivy_operator/vulnerability_handler.py @@ -1,3 +1,6 @@ +import contextlib +from datetime import datetime + from dojo.models import Finding from dojo.tools.trivy_operator.uniform_vulnid import UniformTrivyVulnID @@ -35,6 +38,11 @@ def handle_vulns(self, labels, vulnerabilities, test): package_name = vulnerability.get("resource") package_version = vulnerability.get("installedVersion") cvssv3_score = vulnerability.get("score") + published_date = vulnerability.get("publishedDate") + publish_date = None + if published_date: + with contextlib.suppress(ValueError): + publish_date = datetime.strptime(published_date, "%Y-%m-%dT%H:%M:%SZ").date() finding_tags = [resource_namespace] target_target = None target_class = None @@ -86,6 +94,7 @@ def handle_vulns(self, labels, vulnerabilities, test): dynamic_finding=False, service=service, file_path=file_path, + publish_date=publish_date, fix_available=fix_available, ) finding.unsaved_tags = [tag for tag in finding_tags if tag] diff --git a/unittests/scans/trivy_operator/configauditreport_with_remediation.json b/unittests/scans/trivy_operator/configauditreport_with_remediation.json new file mode 100644 index 00000000000..62f90b7db52 --- /dev/null +++ b/unittests/scans/trivy_operator/configauditreport_with_remediation.json @@ -0,0 +1,60 @@ +{ + "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": "Deployment", + "trivy-operator.resource.name": "nginx-app", + "trivy-operator.resource.namespace": "production" + }, + "name": "deployment-nginx-app", + "namespace": "production", + "resourceVersion": "5678", + "uid": "test-with-remediation" + }, + "report": { + "checks": [ + { + "category": "Kubernetes Security Check", + "checkID": "KSV014", + "description": "An immutable root file system prevents applications from writing to their local disk.", + "messages": [ + "Container 'nginx' of Deployment 'nginx-app' should set 'securityContext.readOnlyRootFilesystem' to true" + ], + "remediation": "Set 'securityContext.readOnlyRootFilesystem' to true.", + "severity": "LOW", + "success": false, + "title": "Root file system is not read-only" + }, + { + "checkID": "KSV003", + "description": "The container should drop all default capabilities and add only those that are needed.", + "messages": [ + "Container 'nginx' of Deployment 'nginx-app' should add 'ALL' to 'securityContext.capabilities.drop'" + ], + "severity": "LOW", + "success": false, + "title": "Default capabilities not dropped" + } + ], + "scanner": { + "name": "Trivy", + "vendor": "Aqua Security", + "version": "0.48.3" + }, + "summary": { + "criticalCount": 0, + "highCount": 0, + "lowCount": 2, + "mediumCount": 0 + }, + "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..33181a053c8 100644 --- a/unittests/tools/test_trivy_operator_parser.py +++ b/unittests/tools/test_trivy_operator_parser.py @@ -1,3 +1,4 @@ +from datetime import date from dojo.models import Test from dojo.tools.trivy_operator.parser import TrivyOperatorParser @@ -130,6 +131,9 @@ def test_vulnerabilityreport_extended(self): self.assertEqual(5.9, finding.cvssv3_score) self.assertEqual("ubuntu:20.04 (ubuntu 20.04)", finding.file_path) self.assertEqual(["lbc", "ubuntu", "os-pkgs"], finding.unsaved_tags) + self.assertEqual(date(2024, 1, 16), finding.publish_date) + # Second vuln has publishedDate "" -> None + self.assertIsNone(findings[1].publish_date) def test_cis_benchmark(self): with sample_path("cis_benchmark.json").open(encoding="utf-8") as test_file: @@ -169,3 +173,22 @@ def test_findings_clustercompliancereport(self): parser = TrivyOperatorParser() findings = parser.get_findings(test_file, Test()) self.assertEqual(len(findings), 2) + + def test_configauditreport_with_remediation(self): + with sample_path("configauditreport_with_remediation.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 both remediation and messages + finding = findings[0] + self.assertEqual("Low", finding.severity) + self.assertIn("Set 'securityContext.readOnlyRootFilesystem' to true.", finding.mitigation) + self.assertIn("Container 'nginx' of Deployment 'nginx-app'", finding.mitigation) + self.assertEqual(["production", "Kubernetes Security Check"], finding.unsaved_tags) + # Second check: messages only, no remediation or category + finding = findings[1] + self.assertEqual( + "Container 'nginx' of Deployment 'nginx-app' should add 'ALL' to 'securityContext.capabilities.drop'", + finding.mitigation, + ) + self.assertEqual(["production"], finding.unsaved_tags)