Skip to content

Commit cab92de

Browse files
authored
Merge pull request #14139 from manuel-sommer/issue_14136
🎉 add Trivy misconfiguration fields #14136
2 parents a56d3ea + 4920999 commit cab92de

4 files changed

Lines changed: 151 additions & 25 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
title: 'Upgrading to DefectDojo Version 2.54.3'
3+
toc_hide: true
4+
weight: -20250602
5+
description: Trivy parser deduplication
6+
---
7+
8+
## Trivy parser deduplication
9+
Deduplication of Trivy misconfiguration findings is improved for newly imported findings, but existing findings may no longer match because they don’t contain the new vulnerability_id or file_path fields.

dojo/tools/trivy/parser.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -335,52 +335,53 @@ def get_result_items(self, test, results, service_name=None, artifact_name=""):
335335

336336
misconfigurations = target_data.get("Misconfigurations", [])
337337
for misconfiguration in misconfigurations:
338+
misc_id = misconfiguration.get("ID", None)
339+
misc_avdid = misconfiguration.get("AVDID", misc_id)
340+
misc_title = misconfiguration.get("Title", "Unknown Misconfiguration")
338341
misc_type = misconfiguration.get("Type")
339-
misc_id = misconfiguration.get("ID")
340-
misc_title = misconfiguration.get("Title")
341-
misc_description = misconfiguration.get("Description")
342-
misc_message = misconfiguration.get("Message")
342+
misc_description = misconfiguration.get("Description", "")
343+
misc_message = misconfiguration.get("Message", "")
343344
misc_resolution = misconfiguration.get("Resolution")
344-
misc_severity = misconfiguration.get("Severity")
345+
misc_severity = misconfiguration.get("Severity", "Low")
345346
misc_primary_url = misconfiguration.get("PrimaryURL")
346347
misc_references = misconfiguration.get("References", [])
347-
misc_causemetadata = misconfiguration.get("CauseMetadata", {})
348-
misc_cause_code = misc_causemetadata.get("Code", {})
349-
misc_cause_lines = misc_cause_code.get("Lines", [])
350-
string_lines_table = self.get_lines_as_string_table(misc_cause_lines)
348+
causemeta = misconfiguration.get("CauseMetadata", {})
349+
cause_code = causemeta.get("Code", {})
350+
cause_lines = cause_code.get("Lines", [])
351+
string_lines_table = self.get_lines_as_string_table(cause_lines)
351352
if string_lines_table:
352-
misc_message += ("\n" + string_lines_table)
353-
354-
title = f"{misc_id} - {misc_title}"
353+
misc_message += "\n" + string_lines_table
355354
description = MISC_DESCRIPTION_TEMPLATE.format(
356355
target=target_target,
357356
type=misc_type,
358357
description=misc_description,
359358
message=misc_message,
360359
)
361-
severity = TRIVY_SEVERITIES[misc_severity]
362-
references = None
360+
refs = []
363361
if misc_primary_url:
364-
references = f"{misc_primary_url}\n"
365-
if misc_primary_url in misc_references:
366-
misc_references.remove(misc_primary_url)
367-
if references:
368-
references += "\n".join(misc_references)
369-
else:
370-
references = "\n".join(misc_references)
371-
362+
refs.append(misc_primary_url)
363+
refs.extend(r for r in misc_references if r != misc_primary_url)
364+
references = "\n".join(refs) if refs else None
365+
severity = TRIVY_SEVERITIES.get(misc_severity, "Info")
366+
file_path = target_target
372367
finding = Finding(
373368
test=test,
374-
title=title,
369+
title=f"{misc_id} - {misc_title}",
375370
severity=severity,
376-
references=references,
377371
description=description,
378372
mitigation=misc_resolution,
373+
references=references,
374+
url=misc_primary_url,
375+
file_path=file_path,
376+
impact=misc_description,
379377
fix_available=True,
380378
static_finding=True,
381379
dynamic_finding=False,
382380
service=service_name,
383381
)
382+
if misc_avdid:
383+
finding.unsaved_vulnerability_ids = []
384+
finding.unsaved_vulnerability_ids.append(misc_avdid)
384385
finding.unsaved_tags = [target_type, target_class]
385386
items.append(finding)
386387

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
{
2+
"SchemaVersion": 2,
3+
"ReportID": "019bdfd9-f774-7da7-917a-6b15e3b0b2a0",
4+
"CreatedAt": "2026-01-21T10:19:22.484900675+01:00",
5+
"ArtifactName": "tofu/clusters/sandbox",
6+
"ArtifactType": "filesystem",
7+
"Results": [
8+
{
9+
"Target": "../../aws-credentials/main.tf",
10+
"Class": "config",
11+
"Type": "terraform",
12+
"MisconfSummary": {
13+
"Successes": 0,
14+
"Failures": 1
15+
},
16+
"Misconfigurations": [
17+
{
18+
"Type": "Terraform Security Check",
19+
"ID": "AVD-AWS-0143",
20+
"AVDID": "AVD-AWS-0143",
21+
"Title": "IAM policies should not be granted directly to users.",
22+
"Description": "CIS recommends that you apply IAM policies directly to groups and roles but not users. Assigning privileges at the group or role level reduces the complexity of access management as the number of users grow. Reducing access management complexity might in turn reduce opportunity for a principal to inadvertently receive or retain excessive privileges.\n",
23+
"Message": "One or more policies are attached directly to a user",
24+
"Namespace": "builtin.aws.iam.aws0143",
25+
"Query": "data.builtin.aws.iam.aws0143.deny",
26+
"Resolution": "Grant policies at the group level instead.",
27+
"Severity": "LOW",
28+
"PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0143",
29+
"References": [
30+
"https://console.aws.amazon.com/iam/",
31+
"https://avd.aquasec.com/misconfig/avd-aws-0143"
32+
],
33+
"Status": "FAIL",
34+
"CauseMetadata": {
35+
"Resource": "module.aws_credentials",
36+
"Provider": "AWS",
37+
"Service": "iam",
38+
"StartLine": 20,
39+
"EndLine": 24,
40+
"Code": {
41+
"Lines": [
42+
{
43+
"Number": 20,
44+
"Content": "resource \"aws_iam_user\" \"this\" {",
45+
"IsCause": true,
46+
"Annotation": "",
47+
"Truncated": false,
48+
"Highlighted": "\u001b[38;5;33mresource\u001b[0m \u001b[38;5;37m\"aws_iam_user\"\u001b[0m \u001b[38;5;37m\"this\"\u001b[0m {",
49+
"FirstCause": true,
50+
"LastCause": false
51+
},
52+
{
53+
"Number": 21,
54+
"Content": " for_each = var.users",
55+
"IsCause": true,
56+
"Annotation": "",
57+
"Truncated": false,
58+
"Highlighted": " \u001b[38;5;245mfor_each\u001b[0m = \u001b[38;5;33mvar\u001b[0m.users",
59+
"FirstCause": false,
60+
"LastCause": false
61+
},
62+
{
63+
"Number": 22,
64+
"Content": "",
65+
"IsCause": true,
66+
"Annotation": "",
67+
"Truncated": false,
68+
"FirstCause": false,
69+
"LastCause": false
70+
},
71+
{
72+
"Number": 23,
73+
"Content": " name = \"cluster-${var.cluster_name}-${each.key}\"",
74+
"IsCause": true,
75+
"Annotation": "",
76+
"Truncated": false,
77+
"Highlighted": " \u001b[38;5;245mname\u001b[0m = \u001b[38;5;37m\"cluster-\u001b[0m\u001b[38;5;37m${\u001b[0m\u001b[38;5;33mvar\u001b[0m.cluster_name\u001b[38;5;37m}\u001b[0m\u001b[38;5;37m-\u001b[0m\u001b[38;5;37m${\u001b[0m\u001b[38;5;33meach\u001b[0m.key\u001b[38;5;37m}\u001b[0m\u001b[38;5;37m\"",
78+
"FirstCause": false,
79+
"LastCause": false
80+
},
81+
{
82+
"Number": 24,
83+
"Content": "}",
84+
"IsCause": true,
85+
"Annotation": "",
86+
"Truncated": false,
87+
"Highlighted": "\u001b[0m}",
88+
"FirstCause": false,
89+
"LastCause": true
90+
}
91+
]
92+
},
93+
"Occurrences": [
94+
{
95+
"Resource": "module.aws_credentials",
96+
"Filename": "main.tf",
97+
"Location": {
98+
"StartLine": 183,
99+
"EndLine": 248
100+
}
101+
}
102+
]
103+
}
104+
}
105+
]
106+
}
107+
]
108+
}

unittests/tools/test_trivy_parser.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def test_kubernetes(self):
170170
re_finding_description = re.sub(r"\s+", " ", finding.description)
171171
self.assertEqual(re_description.strip(), re_finding_description.strip())
172172
self.assertEqual("Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'.", finding.mitigation)
173-
self.assertIsNone(finding.unsaved_vulnerability_ids)
173+
self.assertEqual(finding.unsaved_vulnerability_ids, ["KSV001"])
174174
self.assertEqual(["kubernetes", "config"], finding.unsaved_tags)
175175
self.assertIsNone(finding.component_name)
176176
self.assertIsNone(finding.component_version)
@@ -337,3 +337,11 @@ def test_severity_prio(self):
337337
self.assertEqual("Critical", finding.severity)
338338
self.assertEqual("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N", finding.cvssv3)
339339
self.assertEqual(7.5, finding.cvssv3_score)
340+
341+
def test_misconfig_fields(self):
342+
# this tests issue #14136. The unittest file is just a copy of cvss_severity_source.json with edited severities
343+
with sample_path("issue_14136.json").open(encoding="utf-8") as test_file:
344+
parser = TrivyParser()
345+
findings = parser.get_findings(test_file, Test())
346+
self.assertEqual(len(findings), 1)
347+
self.assertEqual("Low", findings[0].severity)

0 commit comments

Comments
 (0)