Skip to content

Commit 46ef61b

Browse files
authored
feat(trivy_operator): add remediation, messages, category, and publishedDate mappings (#14360)
Add missing field mappings to the Trivy Operator parser: - checks_handler: extract remediation and messages → Finding.mitigation, category → Finding tag. These fields are defined on the Check struct (config_audit_types.go) and were already mapped in compliance_handler but missing from checks_handler. Messages and remediation are combined in the mitigation field since both provide actionable guidance. Category is stored as a tag for filterability rather than in description, to avoid affecting the deduplication hash. - vulnerability_handler: extract publishedDate → Finding.publish_date. Trivy provides CVE publication dates in VulnerabilityReport; map them to the existing nullable DateField with graceful handling of empty strings and malformed dates. Signed-off-by: Sergiy Kulanov <sergiy_kulanov@epam.com>
1 parent f58a0c9 commit 46ef61b

4 files changed

Lines changed: 105 additions & 2 deletions

File tree

dojo/tools/trivy_operator/checks_handler.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,36 @@ def handle_checks(self, labels, checks, test):
3030
"https://avd.aquasec.com/misconfig/kubernetes/"
3131
+ check_id.lower()
3232
)
33+
check_remediation = check.get("remediation", "")
3334
check_description = check.get("description", "")
35+
check_messages = check.get("messages", [])
36+
check_category = check.get("category", "")
3437
check_description += "\n**container.name:** " + container_name
3538
check_description += "\n**resource.kind:** " + resource_kind
3639
check_description += "\n**resource.name:** " + resource_name
3740
check_description += "\n**resource.namespace:** " + resource_namespace
41+
mitigation = check_remediation or None
42+
if check_messages:
43+
messages_text = "\n".join(check_messages)
44+
if mitigation:
45+
mitigation += "\n\n" + messages_text
46+
else:
47+
mitigation = messages_text
3848
title = f"{check_id} - {check_title}"
3949
finding = Finding(
4050
test=test,
4151
title=title,
4252
severity=check_severity,
53+
mitigation=mitigation,
4354
references=check_references,
4455
description=check_description,
4556
static_finding=True,
4657
dynamic_finding=False,
4758
service=service,
4859
fix_available=True,
4960
)
50-
if resource_namespace:
51-
finding.unsaved_tags = [resource_namespace]
61+
finding_tags = [resource_namespace, check_category]
62+
finding.unsaved_tags = [tag for tag in finding_tags if tag]
5263
if check_id:
5364
finding.unsaved_vulnerability_ids = [UniformTrivyVulnID().return_uniformed_vulnid(check_id)]
5465
findings.append(finding)

dojo/tools/trivy_operator/vulnerability_handler.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import contextlib
2+
from datetime import datetime
3+
14
from dojo.models import Finding
25
from dojo.tools.trivy_operator.uniform_vulnid import UniformTrivyVulnID
36

@@ -35,6 +38,11 @@ def handle_vulns(self, labels, vulnerabilities, test):
3538
package_name = vulnerability.get("resource")
3639
package_version = vulnerability.get("installedVersion")
3740
cvssv3_score = vulnerability.get("score")
41+
published_date = vulnerability.get("publishedDate")
42+
publish_date = None
43+
if published_date:
44+
with contextlib.suppress(ValueError):
45+
publish_date = datetime.strptime(published_date, "%Y-%m-%dT%H:%M:%SZ").date()
3846
finding_tags = [resource_namespace]
3947
target_target = None
4048
target_class = None
@@ -86,6 +94,7 @@ def handle_vulns(self, labels, vulnerabilities, test):
8694
dynamic_finding=False,
8795
service=service,
8896
file_path=file_path,
97+
publish_date=publish_date,
8998
fix_available=fix_available,
9099
)
91100
finding.unsaved_tags = [tag for tag in finding_tags if tag]
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"apiVersion": "aquasecurity.github.io/v1alpha1",
3+
"kind": "ConfigAuditReport",
4+
"metadata": {
5+
"annotations": {
6+
"trivy-operator.aquasecurity.github.io/report-ttl": "24h0m0s"
7+
},
8+
"creationTimestamp": "2023-03-23T16:22:54Z",
9+
"generation": 1,
10+
"labels": {
11+
"plugin-config-hash": "659b7b9c46",
12+
"resource-spec-hash": "fc85b485f",
13+
"trivy-operator.resource.kind": "Deployment",
14+
"trivy-operator.resource.name": "nginx-app",
15+
"trivy-operator.resource.namespace": "production"
16+
},
17+
"name": "deployment-nginx-app",
18+
"namespace": "production",
19+
"resourceVersion": "5678",
20+
"uid": "test-with-remediation"
21+
},
22+
"report": {
23+
"checks": [
24+
{
25+
"category": "Kubernetes Security Check",
26+
"checkID": "KSV014",
27+
"description": "An immutable root file system prevents applications from writing to their local disk.",
28+
"messages": [
29+
"Container 'nginx' of Deployment 'nginx-app' should set 'securityContext.readOnlyRootFilesystem' to true"
30+
],
31+
"remediation": "Set 'securityContext.readOnlyRootFilesystem' to true.",
32+
"severity": "LOW",
33+
"success": false,
34+
"title": "Root file system is not read-only"
35+
},
36+
{
37+
"checkID": "KSV003",
38+
"description": "The container should drop all default capabilities and add only those that are needed.",
39+
"messages": [
40+
"Container 'nginx' of Deployment 'nginx-app' should add 'ALL' to 'securityContext.capabilities.drop'"
41+
],
42+
"severity": "LOW",
43+
"success": false,
44+
"title": "Default capabilities not dropped"
45+
}
46+
],
47+
"scanner": {
48+
"name": "Trivy",
49+
"vendor": "Aqua Security",
50+
"version": "0.48.3"
51+
},
52+
"summary": {
53+
"criticalCount": 0,
54+
"highCount": 0,
55+
"lowCount": 2,
56+
"mediumCount": 0
57+
},
58+
"updateTimestamp": "2023-03-23T16:22:54Z"
59+
}
60+
}

unittests/tools/test_trivy_operator_parser.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import date
12

23
from dojo.models import Test
34
from dojo.tools.trivy_operator.parser import TrivyOperatorParser
@@ -130,6 +131,9 @@ def test_vulnerabilityreport_extended(self):
130131
self.assertEqual(5.9, finding.cvssv3_score)
131132
self.assertEqual("ubuntu:20.04 (ubuntu 20.04)", finding.file_path)
132133
self.assertEqual(["lbc", "ubuntu", "os-pkgs"], finding.unsaved_tags)
134+
self.assertEqual(date(2024, 1, 16), finding.publish_date)
135+
# Second vuln has publishedDate "" -> None
136+
self.assertIsNone(findings[1].publish_date)
133137

134138
def test_cis_benchmark(self):
135139
with sample_path("cis_benchmark.json").open(encoding="utf-8") as test_file:
@@ -169,3 +173,22 @@ def test_findings_clustercompliancereport(self):
169173
parser = TrivyOperatorParser()
170174
findings = parser.get_findings(test_file, Test())
171175
self.assertEqual(len(findings), 2)
176+
177+
def test_configauditreport_with_remediation(self):
178+
with sample_path("configauditreport_with_remediation.json").open(encoding="utf-8") as test_file:
179+
parser = TrivyOperatorParser()
180+
findings = parser.get_findings(test_file, Test())
181+
self.assertEqual(len(findings), 2)
182+
# First check: has both remediation and messages
183+
finding = findings[0]
184+
self.assertEqual("Low", finding.severity)
185+
self.assertIn("Set 'securityContext.readOnlyRootFilesystem' to true.", finding.mitigation)
186+
self.assertIn("Container 'nginx' of Deployment 'nginx-app'", finding.mitigation)
187+
self.assertEqual(["production", "Kubernetes Security Check"], finding.unsaved_tags)
188+
# Second check: messages only, no remediation or category
189+
finding = findings[1]
190+
self.assertEqual(
191+
"Container 'nginx' of Deployment 'nginx-app' should add 'ALL' to 'securityContext.capabilities.drop'",
192+
finding.mitigation,
193+
)
194+
self.assertEqual(["production"], finding.unsaved_tags)

0 commit comments

Comments
 (0)