Skip to content

Commit 28089fc

Browse files
🎉 fix parser anchore engine to parse new report format #11552 (#12020)
* 🎉 fix parser anchore engine new report format #11552 * first shot * fixes * advance unittests
1 parent 6200350 commit 28089fc

File tree

4 files changed

+1196
-27
lines changed

4 files changed

+1196
-27
lines changed

docs/content/en/connecting_your_tools/parsers/file/anchore_engine.md

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,7 @@ DefectDojo parser accepts a .json file.
99
Using the [Anchore CLI](https://docs.anchore.com/current/docs/using/cli_usage/images/inspecting_image_content/) is the most reliable way to generate an Anchore report which DefectDojo can parse. When generating a report with the Anchore CLI, please use the following command to ensure complete data: `anchore-cli --json image vuln <image:tag> all`
1010

1111
### Acceptable JSON Format
12-
All properties are strings and are required by the parser.
13-
14-
~~~
15-
16-
{
17-
"imageDigest": "sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
18-
"vulnerabilities": [
19-
{
20-
"feed": "example-feed",
21-
"feed_group": "example-feed-group",
22-
"fix": "1.2.4",
23-
"package": "example-package",
24-
"package_cpe": "cpe:2.3:a:*:example:1.2.3:*:*:*:*:*:*:*",
25-
"package_name": "example-package-name",
26-
"package_path": "path/to/package",
27-
"package_type": "dpkg",
28-
"package_version": "1.2.3",
29-
"severity": "Medium",
30-
"url": "https://example.com/cve/CVE-2011-3389",
31-
"vuln": "CVE-2011-3389"
32-
},
33-
...
34-
],
35-
"vulnerability_type": "os"
36-
}
37-
~~~
12+
All properties are strings and are required by the parser. As the parser evolved, two anchore engine parser JSON formats are present till now. Both ([old](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/anchore_engine/many_vulns.json) / [new](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/anchore_engine/new_format_issue_11552.json)) are supported.
3813

3914
### Sample Scan Data
4015
Sample Anchore-Engine scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/anchore_engine).

dojo/tools/anchore_engine/parser.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,88 @@ def get_description_for_scan_types(self, scan_type):
1414
return "Anchore-CLI JSON vulnerability report format."
1515

1616
def get_findings(self, filename, test):
17-
data = json.load(filename)
17+
try:
18+
data = json.load(filename)
19+
except AttributeError:
20+
with open(filename, encoding="utf-8") as file:
21+
data = json.load(file)
22+
if data.get("metadata"):
23+
return self.get_findings_with_metadata(data, test)
24+
return self.get_findings_without_metadata(data, test)
25+
26+
def get_findings_with_metadata(self, data, test):
27+
dupes = {}
28+
metadata = data.get("metadata", {})
29+
details = f"**Image hash**: {metadata.get('imageDigest', metadata.get('image_digest', 'None'))} \n\n"
30+
31+
for item in data.get("securityEvaluation", []):
32+
vulnerability_id = item.get("vulnerabilityId", "Unknown")
33+
34+
title = (
35+
vulnerability_id + "-" + item.get("package", "Unknown") + "(" + item.get("packageType", "Unknown") + ")"
36+
)
37+
38+
details += f"**Package**: {item.get('package', 'Unknown')}\n\n"
39+
details += f"**Package path**: {item.get('path', 'Unknown')}\n\n"
40+
details += f"**Package type**: {item.get('packageType', 'Unknown')}\n\n"
41+
42+
severity = item.get("severity", "Unknown")
43+
44+
if severity.lower() in {"negligible", "unknown"}:
45+
severity = "Info"
46+
47+
mitigation = "No fix available."
48+
49+
if item.get("fixAvailable") and item["fixAvailable"] != "None":
50+
mitigation = f"Upgrade to: {' or '.join(item['fixAvailable'].split(','))}\n\n"
51+
mitigation += f"URL: {item.get('link', 'None')}"
52+
cvssv3_base_score = item.get("nvdCvssBaseScore")
53+
54+
if isinstance(cvssv3_base_score, str) and cvssv3_base_score.replace(".", "", 1).isdigit():
55+
cvssv3_base_score = float(cvssv3_base_score)
56+
elif not isinstance(cvssv3_base_score, int | float):
57+
cvssv3_base_score = None
58+
59+
references = item.get("link")
60+
61+
dupe_key = "|".join(
62+
[
63+
item.get("cves", "None"),
64+
item.get("package", "None"),
65+
item.get("packageType", "None"),
66+
item.get("path", "None"),
67+
item.get("severity", "None"),
68+
],
69+
)
70+
71+
if dupe_key in dupes:
72+
find = dupes[dupe_key]
73+
else:
74+
find = Finding(
75+
title=title,
76+
test=test,
77+
cve=item.get("cves"),
78+
cvssv3_score=cvssv3_base_score,
79+
description=details,
80+
severity=severity,
81+
mitigation=mitigation,
82+
references=references,
83+
file_path=item.get("path"),
84+
component_name=item.get("package"),
85+
url=item.get("link"),
86+
static_finding=True,
87+
dynamic_finding=False,
88+
vuln_id_from_tool=vulnerability_id,
89+
)
90+
91+
if vulnerability_id:
92+
find.unsaved_vulnerability_ids = [vulnerability_id]
93+
94+
dupes[dupe_key] = find
95+
96+
return list(dupes.values())
97+
98+
def get_findings_without_metadata(self, data, test):
1899
dupes = {}
19100
for item in data["vulnerabilities"]:
20101
vulnerability_id = item.get("vuln")

0 commit comments

Comments
 (0)