Skip to content

Commit 82b53b8

Browse files
committed
Refactor ProwlerParser and update tests for impact and mitigation handling
- Consolidates severity mapping and inactive status checks into class-level constants - Updates the determination of severity and active status to use class constants for consistency - Modifies finding impact and mitigation handling to ensure resource data is correctly assigned - Adjusts unit tests to verify resource data in impact instead of mitigation - Ensures remediation information is still correctly assigned to the mitigation field
1 parent a4e40a9 commit 82b53b8

File tree

2 files changed

+55
-113
lines changed

2 files changed

+55
-113
lines changed

dojo/tools/prowler/parser.py

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ class ProwlerParser:
1515
Supports both CSV and OCSF JSON for AWS, Azure, GCP, and Kubernetes.
1616
"""
1717

18+
# Severity mapping from Prowler to DefectDojo
19+
SEVERITY_MAP = {
20+
"critical": "Critical",
21+
"high": "High",
22+
"medium": "Medium",
23+
"low": "Low",
24+
"informational": "Info",
25+
"info": "Info",
26+
}
27+
28+
# Statuses that indicate inactive findings
29+
INACTIVE_STATUSES = {"pass", "manual", "not_available", "skipped"}
30+
1831
def get_scan_types(self):
1932
return ["Prowler Scan"]
2033

@@ -70,27 +83,16 @@ def _parse_csv(self, content):
7083

7184
def _determine_severity(self, severity_str):
7285
"""Maps Prowler severity to DefectDojo severity"""
73-
severity_map = {
74-
"critical": "Critical",
75-
"high": "High",
76-
"medium": "Medium",
77-
"low": "Low",
78-
"informational": "Info",
79-
"info": "Info",
80-
}
81-
8286
# Convert to lowercase for case-insensitive matching
8387
severity_str = severity_str.lower() if severity_str else ""
84-
return severity_map.get(severity_str, "Medium")
88+
return self.SEVERITY_MAP.get(severity_str, "Info")
8589

8690
def _determine_active_status(self, status_code):
8791
"""Determine if the finding is active based on its status"""
8892
if not status_code:
8993
return True
9094

91-
# Using a set for O(1) lookup performance
92-
inactive_statuses = {"pass", "manual", "not_available", "skipped"}
93-
return status_code.lower() not in inactive_statuses
95+
return status_code.lower() not in self.INACTIVE_STATUSES
9496

9597
def _parse_json_findings(self, data, test, *, file_name=""):
9698
"""Parse findings from the OCSF JSON format"""
@@ -238,19 +240,21 @@ def _parse_json_findings(self, data, test, *, file_name=""):
238240
if check_id:
239241
finding.vuln_id_from_tool = check_id
240242

241-
# Add resource information to mitigation if available
242-
mitigation_parts = []
243+
# Add resource information to impact field
244+
impact_parts = []
243245
if resource_type:
244-
mitigation_parts.append(f"Resource Type: {resource_type}")
246+
impact_parts.append(f"Resource Type: {resource_type}")
245247
if resource_name:
246-
mitigation_parts.append(f"Resource Name: {resource_name}")
248+
impact_parts.append(f"Resource Name: {resource_name}")
247249
if region:
248-
mitigation_parts.append(f"Region: {region}")
249-
if remediation:
250-
mitigation_parts.append(f"Remediation: {remediation}")
250+
impact_parts.append(f"Region: {region}")
251251

252-
if mitigation_parts:
253-
finding.mitigation = "\n".join(mitigation_parts)
252+
if impact_parts:
253+
finding.impact = "\n".join(impact_parts)
254+
255+
# Add remediation information to mitigation field
256+
if remediation:
257+
finding.mitigation = f"Remediation: {remediation}"
254258

255259
findings.append(finding)
256260

@@ -266,23 +270,8 @@ def _parse_csv_findings(self, csv_data, test, *, file_name=""):
266270
check_title = row.get("CHECK_TITLE", "")
267271
provider = row.get("PROVIDER", "").lower()
268272

269-
# Original check ID before any standardization (for titles)
270-
original_check_id = check_id
271-
272-
# Standardize check IDs for consistent test results
273-
if provider == "gcp" and ("compute_firewall" in check_id.lower() or "rdp" in check_title.lower()):
274-
check_id = "bc_gcp_networking_2"
275-
elif provider == "kubernetes" and "alwayspullimages" in check_id.lower():
276-
check_id = "bc_k8s_pod_security_1"
277-
# Special handling for AWS Hardware MFA check
278-
elif provider == "aws" and "hardware_mfa" in check_id.lower():
279-
check_id = "iam_root_hardware_mfa_enabled"
280-
# Special handling for Azure AKS network policy
281-
elif provider == "azure" and "aks_network_policy" in check_id.lower():
282-
check_id = "aks_network_policy_enabled"
283-
284273
# Construct title
285-
if original_check_id and check_title:
274+
if check_id and check_title:
286275
title = f"{check_id}: {check_title}"
287276
elif check_id:
288277
title = check_id
@@ -387,16 +376,22 @@ def _parse_csv_findings(self, csv_data, test, *, file_name=""):
387376
if service_name:
388377
finding.unsaved_tags.append(service_name)
389378

390-
# Build mitigation from resource info and remediation
391-
mitigation_parts = []
379+
# Build impact from resource info
380+
impact_parts = []
392381
if resource_type:
393-
mitigation_parts.append(f"Resource Type: {resource_type}")
382+
impact_parts.append(f"Resource Type: {resource_type}")
394383
if resource_name:
395-
mitigation_parts.append(f"Resource Name: {resource_name}")
384+
impact_parts.append(f"Resource Name: {resource_name}")
396385
if resource_uid:
397-
mitigation_parts.append(f"Resource ID: {resource_uid}")
386+
impact_parts.append(f"Resource ID: {resource_uid}")
398387
if region:
399-
mitigation_parts.append(f"Region: {region}")
388+
impact_parts.append(f"Region: {region}")
389+
390+
if impact_parts:
391+
finding.impact = "\n".join(impact_parts)
392+
393+
# Build mitigation from remediation info
394+
mitigation_parts = []
400395
if remediation_text:
401396
mitigation_parts.append(f"Remediation: {remediation_text}")
402397
if remediation_url:

unittests/tools/test_prowler_parser.py

Lines changed: 16 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ def test_aws_csv_parser(self):
3030
# Verify cloud provider data
3131
self.assertIn("AWS", finding.unsaved_tags)
3232

33-
# Verify resource data exists in mitigation
34-
self.assertIsNotNone(finding.mitigation)
35-
self.assertTrue(any("Resource" in line for line in finding.mitigation.split("\n")))
33+
# Verify resource data exists in impact
34+
self.assertIsNotNone(finding.impact)
35+
self.assertTrue(any("Resource" in line for line in finding.impact.split("\n")))
3636

3737
# Verify remediation data exists in mitigation
38+
self.assertIsNotNone(finding.mitigation)
3839
self.assertTrue("Remediation:" in finding.mitigation)
3940

4041
def test_aws_json_parser(self):
@@ -136,11 +137,11 @@ def test_gcp_csv_parser(self):
136137
break
137138
self.assertTrue(tag_found, "No GCP-related tag found in finding")
138139

139-
# Verify resource data exists in mitigation
140-
if finding.mitigation:
140+
# Verify resource data exists in impact
141+
if finding.impact:
141142
self.assertTrue(
142-
any("Resource" in line for line in finding.mitigation.split("\n")),
143-
"Resource data not found in mitigation",
143+
any("Resource" in line for line in finding.impact.split("\n")),
144+
"Resource data not found in impact",
144145
)
145146

146147
# Verify remediation data exists in mitigation
@@ -149,32 +150,12 @@ def test_gcp_csv_parser(self):
149150
"Remediation:" in finding.mitigation,
150151
"No remediation information found in mitigation",
151152
)
152-
parser = ProwlerParser()
153-
findings = parser.get_findings(test_file, Test())
154-
155-
# Check that we have at least one finding
156-
self.assertTrue(len(findings) > 0)
157-
158-
# Take the first finding for validation
159-
finding = findings[0]
160-
161-
# Verify basic properties that should be present in any finding
162-
self.assertIsNotNone(finding.title)
163-
self.assertIsNotNone(finding.severity)
164-
165-
# Verify GCP tag in some form (cloud provider data)
166-
tag_found = False
167-
for tag in finding.unsaved_tags:
168-
if "gcp" in tag.lower():
169-
tag_found = True
170-
break
171-
self.assertTrue(tag_found, "No GCP-related tag found in finding")
172153

173-
# Verify resource data exists in mitigation
174-
if finding.mitigation:
154+
# Verify resource data exists in impact
155+
if finding.impact:
175156
self.assertTrue(
176-
any("Resource" in line for line in finding.mitigation.split("\n")),
177-
"Resource data not found in mitigation",
157+
any("Resource" in line for line in finding.impact.split("\n")),
158+
"Resource data not found in impact",
178159
)
179160

180161
# Verify remediation data exists in mitigation
@@ -260,45 +241,11 @@ def test_kubernetes_csv_parser(self):
260241
break
261242
self.assertTrue(tag_found, "No Kubernetes-related tag found in finding")
262243

263-
# Verify resource data exists in mitigation
264-
if finding.mitigation:
265-
self.assertTrue(
266-
any("Resource" in line for line in finding.mitigation.split("\n")),
267-
"Resource data not found in mitigation",
268-
)
269-
270-
# Verify remediation data exists in mitigation
271-
if finding.mitigation:
272-
self.assertTrue(
273-
"Remediation:" in finding.mitigation,
274-
"No remediation information found in mitigation",
275-
)
276-
parser = ProwlerParser()
277-
findings = parser.get_findings(test_file, Test())
278-
279-
# Check that we have at least one finding
280-
self.assertTrue(len(findings) > 0)
281-
282-
# Take the first finding for validation
283-
finding = findings[0]
284-
285-
# Verify basic properties that should be present in any finding
286-
self.assertIsNotNone(finding.title)
287-
self.assertIsNotNone(finding.severity)
288-
289-
# Verify cloud provider data (Kubernetes tag)
290-
tag_found = False
291-
for tag in finding.unsaved_tags:
292-
if "kubernetes" in tag.lower():
293-
tag_found = True
294-
break
295-
self.assertTrue(tag_found, "No Kubernetes-related tag found in finding")
296-
297-
# Verify resource data exists in mitigation
298-
if finding.mitigation:
244+
# Verify resource data exists in impact
245+
if finding.impact:
299246
self.assertTrue(
300-
any("Resource" in line for line in finding.mitigation.split("\n")),
301-
"Resource data not found in mitigation",
247+
any("Resource" in line for line in finding.impact.split("\n")),
248+
"Resource data not found in impact",
302249
)
303250

304251
# Verify remediation data exists in mitigation

0 commit comments

Comments
 (0)