Skip to content

Commit e369266

Browse files
Maffoochcneill
andauthored
Reimport: Special statuses should be respected from reports (#12249)
* Reimport: Special statuses should be respected from reports * Fixing ruff * Update unittests/tools/test_checkmarx_one_parser.py Co-authored-by: Charles Neill <[email protected]> * Use the correct dict for statuses --------- Co-authored-by: Charles Neill <[email protected]>
1 parent 8791a9a commit e369266

File tree

7 files changed

+554
-14
lines changed

7 files changed

+554
-14
lines changed

dojo/importers/default_reimporter.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,15 @@ def close_old_findings(
264264
# Determine if pushing to jira or if the finding groups are enabled
265265
mitigated_findings = []
266266
for finding in findings:
267+
# Get any status changes that could have occurred earlier in the process
268+
# for special statuses only.
269+
# An example of such is a finding being reported as false positive, and
270+
# reimport makes this change in the database. However, the findings here
271+
# are calculated based from the original values before the reimport, so
272+
# any updates made during reimport are discarded without first getting the
273+
# state of the finding as it stands at this moment
274+
finding.refresh_from_db(fields=["false_p", "risk_accepted", "out_of_scope"])
275+
# Ensure the finding is not already closed
267276
if not finding.mitigated or not finding.is_mitigated:
268277
logger.debug("mitigating finding: %i:%s", finding.id, finding)
269278
self.mitigate_finding(

dojo/tools/checkmarx_one/parser.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ def parse_iac_vulnerabilities(
7171
"description": (
7272
f"{query.get('description')}\n\n"
7373
f"**Category**: {query.get('category')}\n"),
74-
"verified": query.get("state") != "TO_VERIFY",
7574
"test": test,
7675
}
7776
# Iterate over the individual issues
@@ -81,6 +80,8 @@ def parse_iac_vulnerabilities(
8180
date = self._parse_date(instance.get("firstDetectionDate"))
8281
else:
8382
date = self._parse_date(instance.get("lastDetectionDate"))
83+
instance_details = self.determine_state(instance)
84+
instance_details.update(base_finding_details)
8485
# Create the finding object
8586
finding = Finding(
8687
severity=instance.get("severity").title(),
@@ -90,7 +91,7 @@ def parse_iac_vulnerabilities(
9091
f"**Actual Value**: {instance.get('actualValue')}\n"
9192
f"**Expected Value**: {instance.get('expectedValue')}\n"
9293
),
93-
**base_finding_details,
94+
**instance_details,
9495
)
9596
# Add some details to the description
9697
finding.description += (
@@ -174,14 +175,15 @@ def get_node_snippet(nodes: list) -> str:
174175
date = self._parse_date(instance.get("firstFoundDate"))
175176
else:
176177
date = self._parse_date(instance.get("foundDate"))
178+
instance_details = self.determine_state(instance)
179+
instance_details.update(base_finding_details)
177180
# Create the finding object
178181
finding = Finding(
179182
severity=instance.get("severity").title(),
180183
date=date,
181184
file_path=instance.get("destinationFileName"),
182185
line=instance.get("destinationLine"),
183-
verified=instance.get("state") != "TO_VERIFY",
184-
**base_finding_details,
186+
**instance_details,
185187
)
186188
# Add some details to the description
187189
if node_snippet := get_node_snippet(instance.get("nodes", [])):
@@ -219,11 +221,9 @@ def parse_vulnerabilities(
219221
+ "**uri**: " + locations_uri + "\n"
220222
+ "**startLine**: " + str(locations_startLine) + "\n"
221223
+ "**endLine**: " + str(locations_endLine) + "\n",
222-
false_p=False,
223-
duplicate=False,
224-
out_of_scope=False,
225224
static_finding=True,
226225
dynamic_finding=False,
226+
**self.determine_state(result),
227227
)
228228
findings.append(finding)
229229
return findings
@@ -273,6 +273,7 @@ def get_results_sast(
273273
test=test,
274274
static_finding=True,
275275
unique_id_from_tool=unique_id_from_tool,
276+
**self.determine_state(vulnerability),
276277
)
277278

278279
def get_results_kics(
@@ -290,11 +291,11 @@ def get_results_kics(
290291
title=description,
291292
description=description,
292293
severity=vulnerability.get("severity").title(),
293-
verified=vulnerability.get("state") != "TO_VERIFY",
294294
file_path=file_path,
295295
test=test,
296296
static_finding=True,
297297
unique_id_from_tool=unique_id_from_tool,
298+
**self.determine_state(vulnerability),
298299
)
299300

300301
def get_results_sca(
@@ -311,10 +312,10 @@ def get_results_sca(
311312
title=description,
312313
description=description,
313314
severity=vulnerability.get("severity").title(),
314-
verified=vulnerability.get("state") != "TO_VERIFY",
315315
test=test,
316316
static_finding=True,
317317
unique_id_from_tool=unique_id_from_tool,
318+
**self.determine_state(vulnerability),
318319
)
319320
if (cveId := vulnerability.get("cveId")) is not None:
320321
finding.unsaved_vulnerability_ids = [cveId]
@@ -332,3 +333,18 @@ def get_findings(self, file, test):
332333
findings = self.parse_results(test, results)
333334

334335
return findings
336+
337+
def determine_state(self, data: dict) -> dict:
338+
"""
339+
Determine the state of the findings as set by Checkmarx One docs
340+
https://docs.checkmarx.com/en/34965-68516-managing--triaging--vulnerabilities0.html#UUID-bc2397a3-1614-48bc-ff2f-1bc342071c5a_UUID-ad4991d6-161f-f76e-7d04-970f158eff9b
341+
"""
342+
state = data.get("state")
343+
return {
344+
"active": state in {"TO_VERIFY", "PROPOSED_NOT_EXPLOITABLE", "CONFIRMED", "URGENT"},
345+
"verified": state in {"NOT_EXPLOITABLE", "CONFIRMED", "URGENT"},
346+
"false_p": state == "NOT_EXPLOITABLE",
347+
# These are not managed by checkmarx one, but is nice to explicitly set them
348+
"duplicate": False,
349+
"out_of_scope": False,
350+
}

unittests/dojo_test_case.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -513,14 +513,18 @@ def import_scan_with_params(self, filename, scan_type="ZAP Scan", engagement=1,
513513
with (get_unit_tests_path() / filename).open(encoding="utf-8") as testfile:
514514
payload = {
515515
"minimum_severity": minimum_severity,
516-
"active": active,
517-
"verified": verified,
518516
"scan_type": scan_type,
519517
"file": testfile,
520518
"version": "1.0.1",
521519
"close_old_findings": close_old_findings,
522520
}
523521

522+
if active is not None:
523+
payload["active"] = active
524+
525+
if verified is not None:
526+
payload["verified"] = verified
527+
524528
if engagement:
525529
payload["engagement"] = engagement
526530

@@ -669,14 +673,16 @@ def patch_finding_api(self, finding_id, finding_details, push_to_jira=None):
669673
def assert_finding_count_json(self, count, findings_content_json):
670674
self.assertEqual(findings_content_json["count"], count)
671675

672-
def get_test_findings_api(self, test_id, active=None, verified=None, is_mitigated=None, component_name=None, component_version=None, severity=None):
676+
def get_test_findings_api(self, test_id, active=None, verified=None, is_mitigated=None, false_p=None, component_name=None, component_version=None, severity=None):
673677
payload = {"test": test_id}
674678
if active is not None:
675679
payload["active"] = active
676680
if verified is not None:
677681
payload["verified"] = verified
678682
if is_mitigated is not None:
679683
payload["is_mitigated"] = is_mitigated
684+
if false_p is not None:
685+
payload["false_p"] = false_p
680686
if component_name is not None:
681687
payload["component_name"] = component_name
682688
if severity is not None:

0 commit comments

Comments
 (0)