Skip to content

Commit

Permalink
Feature: Checkmarx Cxflow SAST parser (#9719)
Browse files Browse the repository at this point in the history
* draf parser

* fix typo

* draft parser path node

* add parser

* add dedup aglo

* integration docs

* commented unused var

* Revert "Merge remote-tracking branch 'upstream/dev' into feature-checkmarx-cxflow-sast"

This reverts commit b167f2b5205b427ac0b26ae7fd3f6b4667a01cde, reversing
changes made to 5257a25204dbc9e6603b3b64bc1d78eddb824140.

* Revert "Revert "Merge remote-tracking branch 'upstream/dev' into feature-checkmarx-cxflow-sast""

This reverts commit f9cdafb.

* update doc and remove unused var

* update parser

* update parser test

* Revert "update parser test"

This reverts commit c159233.

* fix ruff

* Update .settings.dist.py.sha256sum

* fix ruff

* fix ruff

* fix ruff #n

* trigger ci

* trigger ci

* Fix ruff

---------

Co-authored-by: biennd4 <[email protected]>
Co-authored-by: Cody Maffucci <[email protected]>
  • Loading branch information
3 people authored Jan 15, 2025
1 parent cd12513 commit 7ecef22
Show file tree
Hide file tree
Showing 8 changed files with 1,680 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: "Checkmarx CxFlow SAST"
toc_hide: true
---

CxFlow is a Spring Boot application written by Checkmarx that enables initiations of scans and result orchestration.
CxFlow support interactive with various Checkmarx product.
This parser support JSON format export by bug tracker.

```
#YAML
cx-flow:
bug-tracker:Json
#CLI
--cx-flow.bug-tracker=json
```

- `Checkmarx CxFlow SAST`: JSON report from Checkmarx Cxflow.

### Sample Scan Data
Sample Checkmarx CxFlow SAST scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/checkmarx_cxflow_sast).
2 changes: 2 additions & 0 deletions dojo/settings/settings.dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,7 @@ def saml2_attrib_map_format(dict):
"Legitify Scan": ["title", "endpoints", "severity"],
"ThreatComposer Scan": ["title", "description"],
"Invicti Scan": ["title", "description", "severity"],
"Checkmarx CxFlow SAST": ["vuln_id_from_tool", "file_path", "line"],
"HackerOne Cases": ["title", "severity"],
"KrakenD Audit Scan": ["description", "mitigation", "severity"],
"Red Hat Satellite": ["description", "severity"],
Expand Down Expand Up @@ -1535,6 +1536,7 @@ def saml2_attrib_map_format(dict):
"Legitify Scan": DEDUPE_ALGO_HASH_CODE,
"ThreatComposer Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE,
"Invicti Scan": DEDUPE_ALGO_HASH_CODE,
"Checkmarx CxFlow SAST": DEDUPE_ALGO_HASH_CODE,
"KrakenD Audit Scan": DEDUPE_ALGO_HASH_CODE,
"PTART Report": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
"Red Hat Satellite": DEDUPE_ALGO_HASH_CODE,
Expand Down
Empty file.
149 changes: 149 additions & 0 deletions dojo/tools/checkmarx_cxflow_sast/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import json
import logging

import dateutil.parser

from dojo.models import Finding

logger = logging.getLogger(__name__)


class _PathNode:
def __init__(self, file: str, line: str, column: str, node_object: str, length: str, snippet: str):
self.file = file
self.line = line
self.column = int(column)
self.node_object = node_object
self.length = int(length)
self.snippet = snippet

@classmethod
def from_json_object(cls, data):
return _PathNode(
data.get("file"),
data.get("line"),
data.get("column"),
data.get("object"),
data.get("length"),
data.get("snippet"),
)


class _Path:
def __init__(self, sink: _PathNode, source: _PathNode, state: str, paths: [_PathNode]):
self.sink = sink
self.source = source
self.state = state
self.paths = paths


class CheckmarxCXFlowSastParser:
def __init__(self):
pass

def get_scan_types(self):
return ["Checkmarx CxFlow SAST"]

def get_label_for_scan_types(self, scan_type):
return scan_type # no custom label for now

def get_description_for_scan_types(self, scan_type):
return "Detailed Report. Import all vulnerabilities from checkmarx without aggregation"

def get_findings(self, file, test):
if file.name.strip().lower().endswith(".json"):
return self._get_findings_json(file, test)
# TODO: support CxXML format
logger.warning(f"Not supported file format ${file}")
return []

def _get_findings_json(self, file, test):
data = json.load(file)
findings = []
additional_details = data.get("additionalDetails")
scan_start_date = additional_details.get("scanStartDate")

issues = data.get("xissues", [])

for issue in issues:
vulnerability = issue.get("vulnerability")
status = issue.get("vulnerabilityStatus")
cwe = issue.get("cwe")
description = issue.get("description")
language = issue.get("language")
severity = issue.get("severity")
link = issue.get("link")
filename = issue.get("filename")
similarity_id = issue.get("similarityId")

issue_additional_details = issue.get("additionalDetails")
categories = issue_additional_details.get("categories")
results = issue_additional_details.get("results")

map_paths = {}

for result in results:
# all path nodes exclude sink, source, state
path_keys = sorted(filter(lambda k: isinstance(k, str) and k.isnumeric(), result.keys()))

path = _Path(
sink=_PathNode.from_json_object(result.get("sink")),
source=_PathNode.from_json_object(result.get("source")),
state=result.get("state"),
paths=[result[k] for k in path_keys],
)

map_paths[str(path.source.line)] = path

for detail_key in issue.get("details"):
if detail_key not in map_paths:
logger.warning(f"{detail_key} not found in path, ignore")
else:
detail = map_paths[detail_key]

finding_detail = f"**Category:** {categories}\n"
finding_detail += f"**Language:** {language}\n"
finding_detail += f"**Status:** {status}\n"
finding_detail += f"**Finding link:** [{link}]({link})\n"
finding_detail += f"**Description:** {description}\n"
finding_detail += f"**Source snippet:** `{detail.source.snippet if detail.source is not None else ''}`\n"
finding_detail += f"**Sink snippet:** `{detail.sink.snippet if detail.sink is not None else ''}`\n"

finding = Finding(
title=vulnerability.replace("_", " ") + " " + detail.sink.file.split("/")[
-1] if detail.sink is not None else "",
cwe=int(cwe),
date=dateutil.parser.parse(scan_start_date),
static_finding=True,
test=test,
sast_source_object=detail.source.node_object if detail.source is not None else None,
sast_sink_object=detail.sink.node_object if detail.sink is not None else None,
sast_source_file_path=detail.source.file if detail.source is not None else None,
sast_source_line=detail.source.line if detail.source is not None else None,
vuln_id_from_tool=similarity_id,
severity=severity,
file_path=filename,
line=detail.sink.line,
false_p=issue.get("details")[detail_key].get("falsePositive") or self.is_not_exploitable(
detail.state),
description=finding_detail,
verified=self.is_verify(detail.state),
active=self.is_active(detail.state),
)

findings.append(finding)

return findings

def is_verify(self, state):
# Confirmed, urgent
verifiedStates = ["2", "3"]
return state in verifiedStates

def is_active(self, state):
# To verify, Confirmed, Urgent, Proposed not exploitable
activeStates = ["0", "2", "3", "4"]
return state in activeStates

def is_not_exploitable(self, state):
return state == "1"
192 changes: 192 additions & 0 deletions unittests/scans/checkmarx_cxflow_sast/1-finding.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
{
"projectId": "6",
"team": "CxServer",
"project": "some-example",
"link": "http://CX-FLOW-CLEAN/CxWebClient/ViewerMain.aspx?scanid=1000026&projectid=6",
"files": "1",
"loc": "268",
"scanType": "Full",
"version":"8.9.0.210",
"additionalDetails": {
"flow-summary": {
"High": 1
},
"scanId": "1000026",
"scanStartDate": "Sunday, January 19, 2020 2:40:11 AM"
},
"xissues": [
{
"vulnerability": "Reflected_XSS_All_Clients",
"vulnerabilityStatus": "TO VERIFY",
"similarityId": "14660819",
"cwe": "79",
"description": "",
"language": "Java",
"severity": "High",
"link": "http://CX-FLOW-CLEAN/CxWebClient/ViewerMain.aspx?scanid=1000026&projectid=6&pathid=2",
"filename": "DOS_Login.java",
"falsePositiveCount": 0,
"details": {
"88": {
"falsePositive": false,
"codeSnippet": "username = s.getParser().getRawParameter(USERNAME);",
"comment": ""
}
},
"additionalDetails": {
"recommendedFix": "http://CX-FLOW-CLEAN/CxWebClient/ScanQueryDescription.aspx?queryID=591&queryVersionCode=56110529&queryTitle=Reflected_XSS_All_Clients",
"categories": "PCI DSS v3.2;PCI DSS (3.2) - 6.5.7 - Cross-site scripting (XSS),OWASP Top 10 2013;A3-Cross-Site Scripting (XSS),FISMA 2014;System And Information Integrity,NIST SP 800-53;SI-15 Information Output Filtering (P0),OWASP Top 10 2017;A7-Cross-Site Scripting (XSS)",
"results": [
{
"sink": {
"file": "AnotherFile.java",
"line": "107",
"column": "9",
"object": "username",
"length" : "8",
"snippet" : "+ username + \"' and password = '\" + password + \"'\";"
},
"state": "0",
"source": {
"file": "DOS_Login.java",
"line": "88",
"column": "46",
"object": "getRawParameter",
"length" : "1",
"snippet" : "username = s.getParser().getRawParameter(USERNAME);"
},
"1" : {
"snippet" : "username = s.getParser().getRawParameter(USERNAME);",
"file" : "DOS_Login.java",
"line" : "88",
"column" : "46",
"length" : "1",
"object" : "getRawParameter"
},
"2" : {
"snippet" : "username = s.getParser().getRawParameter(USERNAME);",
"file" : "DOS_Login.java",
"line" : "88",
"column" : "6",
"length" : "8",
"object" : "username"
},
"3" : {
"snippet" : "if (username.equals(\"jeff\") || username.equals(\"dave\"))",
"file" : "DOS_Login.java",
"line" : "92",
"column" : "37",
"length" : "8",
"object" : "username"
},
"4" : {
"snippet" : "if (username.equals(\"jeff\") || username.equals(\"dave\"))",
"file" : "DOS_Login.java",
"line" : "92",
"column" : "10",
"length" : "8",
"object" : "username"
},
"5" : {
"snippet" : "+ username + \"' and password = '\" + password + \"'\";",
"file" : "AnotherFile.java",
"line" : "107",
"column" : "9",
"length" : "8",
"object" : "username"
}
}
],
"CodeBashingLesson" : "https://cxa.codebashing.com/courses/"
},
"allFalsePositive": false
}
],
"unFilteredIssues": [ {
"vulnerability" : "Reflected_XSS_All_Clients",
"vulnerabilityStatus" : "TO VERIFY",
"similarityId" : "14660819",
"cwe" : "79",
"description" : "",
"language" : "Java",
"severity" : "High",
"link" : "http://CX-FLOW-CLEAN/CxWebClient/ViewerMain.aspx?scanid=1000026&projectid=6&pathid=2",
"filename" : "DOS_Login.java",
"gitUrl" : "",
"falsePositiveCount" : 0,
"details" : {
"88" : {
"falsePositive" : false,
"comment" : ""
}
},
"additionalDetails" : {
"recommendedFix" : "http://CX-FLOW-CLEAN/CxWebClient/ScanQueryDescription.aspx?queryID=591&queryVersionCode=56110529&queryTitle=Reflected_XSS_All_Clients",
"categories" : "PCI DSS v3.2;PCI DSS (3.2) - 6.5.7 - Cross-site scripting (XSS),OWASP Top 10 2013;A3-Cross-Site Scripting (XSS),FISMA 2014;System And Information Integrity,NIST SP 800-53;SI-15 Information Output Filtering (P0),OWASP Top 10 2017;A7-Cross-Site Scripting (XSS)",
"results" : [ {
"1" : {
"snippet" : "username = s.getParser().getRawParameter(USERNAME);",
"file" : "DOS_Login.java",
"line" : "88",
"column" : "46",
"length" : "1",
"object" : "getRawParameter"
},
"2" : {
"snippet" : "username = s.getParser().getRawParameter(USERNAME);",
"file" : "DOS_Login.java",
"line" : "88",
"column" : "6",
"length" : "8",
"object" : "username"
},
"3" : {
"snippet" : "if (username.equals(\"jeff\") || username.equals(\"dave\"))",
"file" : "DOS_Login.java",
"line" : "92",
"column" : "37",
"length" : "8",
"object" : "username"
},
"4" : {
"snippet" : "if (username.equals(\"jeff\") || username.equals(\"dave\"))",
"file" : "DOS_Login.java",
"line" : "92",
"column" : "10",
"length" : "8",
"object" : "username"
},
"5" : {
"snippet" : "+ username + \"' and password = '\" + password + \"'\";",
"file" : "AnotherFile.java",
"line" : "107",
"column" : "9",
"length" : "8",
"object" : "username"
},
"sink" : {
"snippet" : "+ username + \"' and password = '\" + password + \"'\";",
"file" : "AnotherFile.java",
"line" : "107",
"column" : "9",
"length" : "8",
"object" : "username"
},
"state" : "0",
"source" : {
"snippet" : "username = s.getParser().getRawParameter(USERNAME);",
"file" : "DOS_Login.java",
"line" : "88",
"column" : "46",
"length" : "1",
"object" : "getRawParameter"
}
} ]
},
"allFalsePositive" : false
} ],
"reportCreationTime":"Sunday, January 19, 2020 2:41:53 AM",
"deepLink":"http://CX-FLOW-CLEAN/CxWebClient/ViewerMain.aspx?scanid=1000026&projectid=6",
"scanTime":"00h:01m:30s",
"sastResults": false
}
Loading

0 comments on commit 7ecef22

Please sign in to comment.