Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated anchore engine parser for upto date output format #11805

Open
wants to merge 3 commits into
base: bugfix
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,35 @@ All properties are strings and are required by the parser.
~~~
{
"imageDigest": "sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"vulnerabilities": [
"metadata":{
"registry":"docker.io",
"repository":"myimage",
"tag":"new",
"imageDigest":
"sha256:100ec0d69914788c491567bccaea0ab9aa50f0ddd00584db7afb264718c010d6",
"timestamp":"2025-01-13T10:09:59.971Z"
},
"securityEvaluation":[
{
"feed": "example-feed",
"feed_group": "example-feed-group",
"fix": "1.2.4",
"package": "example-package",
"package_cpe": "cpe:2.3:a:*:example:1.2.3:*:*:*:*:*:*:*",
"package_name": "example-package-name",
"package_path": "path/to/package",
"package_type": "dpkg",
"package_version": "1.2.3",
"severity": "Medium",
"url": "https://example.com/cve/CVE-2011-3389",
"vuln": "CVE-2011-3389"
"vulnerabilityId":"CVE-2024-50379",
"cves":"CVE-2024-50379",
"severity":"Critical",
"detectedAt":"2025-01-10T15:09:00Z",
"packageType":"Java",
"path":"/aci/base/lib/tomcat-annotations-api-9.0.97.jar",
"package":"tomcat-annotations-api-9.0.97",
"fixAvailable":"10.1.34,11.0.2,9.0.98",
"fixObservedAt":"2025-01-10T15:09:00Z",
"link":"https://nvd.nist.gov/vuln/detail/CVE-2024-50379",
"nvdCvssBaseScore":9.8
},
...
],
"vulnerability_type": "os"
{
"vulnerabilityId":"CVE-2024-56337",
...
...
"nvdCvssBaseScore":9.8}
...
]
}
~~~

Expand Down
138 changes: 46 additions & 92 deletions dojo/tools/anchore_engine/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,127 +14,81 @@ def get_description_for_scan_types(self, scan_type):
return "Anchore-CLI JSON vulnerability report format."

def get_findings(self, filename, test):
data = json.load(filename)
dupes = {}
for item in data["vulnerabilities"]:
vulnerability_id = item.get("vuln")

try:
data = json.load(filename)
except AttributeError:
with open(filename, encoding="utf-8") as file:
data = json.load(file)

metadata = data.get("metadata", {})
details = f"**Image hash**: {metadata.get('imageDigest', metadata.get('image_digest', 'None'))} \n\n"

for item in data.get("securityEvaluation", []):
vulnerability_id = item.get("vulnerabilityId", "Unknown")

title = (
item["vuln"]
+ " - "
+ item["package"]
+ "("
+ item["package_type"]
+ ")"
vulnerability_id + "-" + item.get("package", "Unknown") + "(" + item.get("packageType", "Unknown") + ")"
)

# Finding details information
# depending on version image_digest/imageDigest
findingdetail = (
"**Image hash**: "
+ item.get("image_digest", item.get("imageDigest", "None"))
+ "\n\n"
)
findingdetail += "**Package**: " + item["package"] + "\n\n"
findingdetail += (
"**Package path**: " + item["package_path"] + "\n\n"
)
findingdetail += (
"**Package type**: " + item["package_type"] + "\n\n"
)
findingdetail += (
"**Feed**: " + item["feed"] + "/" + item["feed_group"] + "\n\n"
)
findingdetail += "**CPE**: " + item["package_cpe"] + "\n\n"
findingdetail += (
"**Description**: "
+ item.get("description", "<None>")
+ "\n\n"
)
details += f"**Package**: {item.get('package', 'Unknown')}\n\n"
details += f"**Package path**: {item.get('path', 'Unknown')}\n\n"
details += f"**Package type**: {item.get('packageType', 'Unknown')}\n\n"

sev = item["severity"]
if sev == "Negligible" or sev == "Unknown":
sev = "Info"
severity = item.get("severity", "Unknown")

mitigation = (
"Upgrade to " + item["package_name"] + " " + item["fix"] + "\n"
)
mitigation += "URL: " + item["url"] + "\n"

cvssv3_base_score = None
if item["feed"] == "nvdv2" or item["feed"] == "vulnerabilities":
if "nvd_data" in item and len(item["nvd_data"]) > 0:
cvssv3_base_score = item["nvd_data"][0]["cvss_v3"][
"base_score"
]
else:
# there may be other keys, but taking a best guess here
if "vendor_data" in item and len(item["vendor_data"]) > 0:
# sometimes cvssv3 in 1st element will have -1 for "not
# set", but have data in the 2nd array item
if (
"cvss_v3" in item["vendor_data"][0]
and item["vendor_data"][0]["cvss_v3"]["base_score"]
!= -1
):
cvssv3_base_score = item["vendor_data"][0]["cvss_v3"][
"base_score"
]
elif len(item["vendor_data"]) > 1:
if (
"cvss_v3" in item["vendor_data"][1]
and item["vendor_data"][1]["cvss_v3"]["base_score"]
!= -1
):
cvssv3_base_score = item["vendor_data"][1][
"cvss_v3"
]["base_score"]
# cvssv3 score spec states value should be between 0.0 and 10.0
# anchorage provides a -1.0 in some situations which breaks spec
if (cvssv3_base_score
and ((float(cvssv3_base_score) < 0)
or (float(cvssv3_base_score) > 10))):
if severity.lower() in ["negligible", "unknown"]:
severity = "Info"

mitigation = "No fix available."

if item.get("fixAvailable") and item["fixAvailable"] != "None":
mitigation = f"Upgrade to: {' or '.join(item['fixAvailable'].split(','))}\n\n"
mitigation += f"URL: {item.get('link', 'None')}"
cvssv3_base_score = item.get("nvdCvssBaseScore")

if isinstance(cvssv3_base_score, str) and cvssv3_base_score.replace(".", "", 1).isdigit():
cvssv3_base_score = float(cvssv3_base_score)
elif not isinstance(cvssv3_base_score, int | float):
cvssv3_base_score = None

references = item["url"]
references = item.get("link")

dupe_key = "|".join(
[
item.get(
"image_digest", item.get("imageDigest", "None"),
), # depending on version image_digest/imageDigest
item["feed"],
item["feed_group"],
item["package_name"],
item["package_version"],
item["package_path"],
item["vuln"],
item.get("cves", "None"),
item.get("package", "None"),
item.get("packageType", "None"),
item.get("path", "None"),
item.get("severity", "None"),
],
)

if dupe_key in dupes:
find = dupes[dupe_key]
else:
dupes[dupe_key] = True

find = Finding(
title=title,
test=test,
cve=item.get("cves"),
cvssv3_score=cvssv3_base_score,
description=findingdetail,
severity=sev,
date=item.get("detectedAt"),
description=details,
severity=severity,
mitigation=mitigation,
references=references,
file_path=item["package_path"],
component_name=item["package_name"],
component_version=item["package_version"],
url=item.get("url"),
file_path=item.get("path"),
component_name=item.get("package"),
url=item.get("link"),
static_finding=True,
dynamic_finding=False,
vuln_id_from_tool=item.get("vuln"),
vuln_id_from_tool=vulnerability_id,
)

if vulnerability_id:
find.unsaved_vulnerability_ids = [vulnerability_id]

dupes[dupe_key] = find

return list(dupes.values())
93 changes: 93 additions & 0 deletions dojo/tools/anchore_engine/test_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import json
from collections import namedtuple

# nampedtuple is used to replicate the functionality of the Finding class inside models.
Test = namedtuple("Test", [])


class AnchoreEngineParser:
def get_scan_types(self):
return ["Anchore Engine Scan"]

def get_label_for_scan_types(self, scan_type):
return "Anchore Engine Scan"

def get_description_for_scan_types(self, scan_type):
return "Anchore-CLI JSON vulnerability report format."

def get_findings(self, filename, test):
dupes = {}

with open(filename, encoding="utf-8") as file:
data = json.load(file)

metadata = data.get("metadata", {})

details = f"**Image hash**: {metadata.get('imageDigest', metadata.get('image_digest', 'None'))} \n\n"

for item in data.get("securityEvaluation", []):
vulnerability_id = item.get("vulnerabilityId", "Unknown")

title = f"{vulnerability_id} - {item.get('package', 'Unknown')} ({item.get('packageType', 'Unknown')})"

details += f"**Package**: {item.get('package', 'Unknown')}\n\n"
details += f"**Package path**: {item.get('path', 'Unknown')}\n\n"
details += f"**Package type**: {item.get('packageType', 'Unknown')}\n\n"

severity = item.get("severity", "Unknown")
if severity.lower() in ["negligible", "unknown"]:
severity = "Info"

mitigation = "No fix available."
if item.get("fixAvailable") and item["fixAvailable"] != "None":
mitigation = f"Upgrade to: {' or '.join(item['fixAvailable'].split(','))}\n\n"
mitigation += f"URL: {item.get('link', 'None')}"

cvssv3_base_score = item.get("nvdCvssBaseScore")

if isinstance(cvssv3_base_score, str) and cvssv3_base_score.replace(".", "", 1).isdigit():
cvssv3_base_score = float(cvssv3_base_score)
elif not isinstance(cvssv3_base_score, int | float):
cvssv3_base_score = None

references = item.get("link")

dupe_key = "|".join(
[
item.get("cves", "None"),
item.get("package", "None"),
item.get("packageType", "None"),
item.get("path", "None"),
item.get("severity", "None"),
],
)

if dupe_key in dupes:
find = dupes[dupe_key]
else:
find = {
"title": title,
"cve": item.get("cves"),
"cvssv3_score": cvssv3_base_score,
"date": item.get("detectedAt"),
"description": details,
"severity": severity,
"mitigation": mitigation,
"references": references,
"file_path": item.get("path"),
"component_name": item.get("package"),
"url": item.get("link"),
"static_finding": True,
"dynamic_finding": False,
"vulnerability_id": vulnerability_id,
}

dupes[dupe_key] = find

return list(dupes.values())


# parser = AnchoreEngineParser()
# json_file_path = "~/Vulnerability_Report_2025-01-13T10_09_59.971Z.json"

# findings = parser.get_findings(json_file_path, Test())
Loading
Loading