Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions dojo/settings/settings.dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,7 @@ def saml2_attrib_map_format(din):
"AnchoreCTL Policies Report": True,
"Anchore Enterprise Policy Check": True,
"Anchore Grype": True,
"Anchore Grype detailed": True,
"AWS Prowler Scan": True,
"AWS Prowler V3": True,
"Checkmarx Scan": False,
Expand Down Expand Up @@ -1616,6 +1617,7 @@ def saml2_attrib_map_format(din):
"AnchoreCTL Policies Report": DEDUPE_ALGO_HASH_CODE,
"Anchore Enterprise Policy Check": DEDUPE_ALGO_HASH_CODE,
"Anchore Grype": DEDUPE_ALGO_HASH_CODE,
"Anchore Grype detailed": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
"Aqua Scan": DEDUPE_ALGO_HASH_CODE,
"AuditJS Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
"AWS Prowler Scan": DEDUPE_ALGO_HASH_CODE,
Expand Down
25 changes: 20 additions & 5 deletions dojo/tools/anchore_grype/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,26 @@
"""

def get_scan_types(self):
return ["Anchore Grype"]
return ["Anchore Grype", "Anchore Grype detailed"]

def get_label_for_scan_types(self, scan_type):
return "Anchore Grype"
return scan_type

def get_description_for_scan_types(self, scan_type):
return (
if scan_type == "Anchore Grype":
return (
"A vulnerability scanner for container images, filesystems, and SBOMs. "
"JSON report generated with '--output=json' format."
)
return "Detailed Report. Import all vulnerabilities from Anchore Grype without aggregation, creating a separate finding per file path."

# mode:
# None (default): aggregates findings per CVE, component name and version (legacy behavior)
# 'detailed': no aggregation - creates separate findings per file_path
mode = None

def set_mode(self, mode):
self.mode = mode

def get_findings(self, file, test):
logger.debug("file: %s", file)
Expand Down Expand Up @@ -185,7 +195,10 @@
if finding_epss_score is None and rel_vuln_id:
finding_epss_score, finding_epss_percentile = self.get_epss_values(vuln_id, vuln_epss)

dupe_key = finding_title
if self.mode == "detailed":
dupe_key = f"{vuln_id}|{artifact_name}|{artifact_version}|{file_path}"
else:
dupe_key = finding_title
Comment thread
Kasyap7 marked this conversation as resolved.
Outdated
if dupe_key in dupes:
finding = dupes[dupe_key]
finding.nb_occurences += 1
Expand All @@ -210,7 +223,9 @@
fix_available=fix_available,
fix_version=fix_version,
)
dupes[dupe_key].unsaved_vulnerability_ids = vulnerability_ids
if self.mode == "detailed":
dupes[dupe_key].unique_id_from_tool = dupe_key
dupes[dupe_key].unsaved_vulnerability_ids = vulnerability_ids

Check failure on line 228 in dojo/tools/anchore_grype/parser.py

View workflow job for this annotation

GitHub Actions / ruff-linting

ruff (W291)

dojo/tools/anchore_grype/parser.py:228:78: W291 Trailing whitespace help: Remove trailing whitespace
if settings.V3_FEATURE_LOCATIONS and artifact_purl:
dupes[dupe_key].unsaved_locations.append(
LocationData.dependency(purl=artifact_purl, file_path=file_path),
Expand Down
136 changes: 136 additions & 0 deletions unittests/scans/anchore_grype/same_cve_different_paths.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
{
"matches": [
{
"vulnerability": {
"id": "CVE-2021-33574",
"dataSource": "https://security-tracker.debian.org/tracker/CVE-2021-33574",
"namespace": "debian:10",
"severity": "Critical",
"urls": [
"https://security-tracker.debian.org/tracker/CVE-2021-33574"
],
"cvss": [],
"fix": {
"versions": [],
"state": "not-fixed"
},
"advisories": []
},
"relatedVulnerabilities": [],
"matchDetails": [
{
"matcher": "dpkg-matcher",
"searchedBy": {
"distro": {
"type": "debian",
"version": "10"
},
"namespace": "debian:10",
"package": {
"name": "libc6",
"version": "2.28-10"
}
},
"found": {
"versionConstraint": "none (deb)"
}
}
],
"artifact": {
"name": "libc6",
"version": "2.28-10",
"type": "deb",
"locations": [
{
"path": "/usr/lib/x86_64-linux-gnu/libc.so.6",
"layerID": "sha256:e3d73f29c6746d2b19dbcc7bfa7b2464c4951807237658ad483faa014f9f9187"
}
],
"language": "",
"licenses": [],
"cpes": [],
"purl": "pkg:deb/debian/libc6@2.28-10?arch=amd64",
"metadataType": "DpkgMetadata"
}
},
{
"vulnerability": {
"id": "CVE-2021-33574",
"dataSource": "https://security-tracker.debian.org/tracker/CVE-2021-33574",
"namespace": "debian:10",
"severity": "Critical",
"urls": [
"https://security-tracker.debian.org/tracker/CVE-2021-33574"
],
"cvss": [],
"fix": {
"versions": [],
"state": "not-fixed"
},
"advisories": []
},
"relatedVulnerabilities": [],
"matchDetails": [
{
"matcher": "dpkg-matcher",
"searchedBy": {
"distro": {
"type": "debian",
"version": "10"
},
"namespace": "debian:10",
"package": {
"name": "libc6",
"version": "2.28-10"
}
},
"found": {
"versionConstraint": "none (deb)"
}
}
],
"artifact": {
"name": "libc6",
"version": "2.28-10",
"type": "deb",
"locations": [
{
"path": "/lib/x86_64-linux-gnu/libc.so.6",
"layerID": "sha256:10bf86ff9f6a0d24fa41491e74b8dcdeb8b23cc654a2a5e36573eac5e40ea25c"
}
],
"language": "",
"licenses": [],
"cpes": [],
"purl": "pkg:deb/debian/libc6@2.28-10?arch=amd64",
"metadataType": "DpkgMetadata"
}
}
],
"source": {
"type": "image",
"target": {
"userInput": "test-image:latest",
"imageID": "sha256:test",
"manifestDigest": "sha256:test",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [],
"imageSize": 100,
"layers": [],
"manifest": null,
"config": null,
"repoDigests": [],
"architecture": "amd64",
"os": "linux"
}
},
"distro": {
"name": "debian",
"version": "10",
"idLike": []
},
"descriptor": {
"name": "grype",
"version": "0.36.0"
}
}
25 changes: 25 additions & 0 deletions unittests/tools/test_anchore_grype_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,28 @@ def test_grype_epss_problem(self):
self.assertAlmostEqual(epss_score, finding.epss_score, places=5)
self.assertIsNotNone(finding.epss_percentile)
self.assertAlmostEqual(epss_percentile, finding.epss_percentile, places=5)

def test_parser_scan_types(self):
parser = AnchoreGrypeParser()
scan_types = parser.get_scan_types()
self.assertIn("Anchore Grype", scan_types)
self.assertIn("Anchore Grype detailed", scan_types)

def test_default_mode_deduplicates_same_cve_different_paths(self):
"""In default mode, same CVE/component/version at different file paths should be merged into one finding."""
with (get_unit_tests_scans_path("anchore_grype") / "same_cve_different_paths.json").open(encoding="utf-8") as testfile:
parser = AnchoreGrypeParser()
findings = parser.get_findings(testfile, Test())
self.assertEqual(1, len(findings))
self.assertEqual(findings[0].nb_occurences, 2)

def test_detailed_mode_separates_same_cve_different_paths(self):
"""In detailed mode, same CVE/component/version at different file paths should produce separate findings."""
with (get_unit_tests_scans_path("anchore_grype") / "same_cve_different_paths.json").open(encoding="utf-8") as testfile:
parser = AnchoreGrypeParser()
parser.set_mode("detailed")
findings = parser.get_findings(testfile, Test())
self.assertEqual(2, len(findings))
file_paths = {f.file_path for f in findings}
self.assertIn("/usr/lib/x86_64-linux-gnu/libc.so.6", file_paths)
self.assertIn("/lib/x86_64-linux-gnu/libc.so.6", file_paths)
Loading