-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
New HCL AppScan on Cloud SAST parser #11375
base: dev
Are you sure you want to change the base?
Changes from all commits
1a2fa77
164964c
6791149
7f273cd
b918484
e831fb2
a75285c
d678f47
ac0c66c
4a9644d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
title: "HCL AppScan on Cloud SAST" | ||
toc_hide: true | ||
--- | ||
HCL Appscan on Cloud can export the results in PDF, XML and CSV formats but this parser only supports the import of XML generated from HCL Appscan on Cloud for SAST scans. | ||
|
||
### Sample Scan Data | ||
Sample HCL AppScan on Cloud SAST scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/hcl_asoc_sast). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__author__ = "xpert98" |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,151 @@ | ||||||
from xml.dom import NamespaceErr | ||||||
|
||||||
from defusedxml import ElementTree as ET | ||||||
|
||||||
from dojo.models import Finding | ||||||
|
||||||
|
||||||
class HCLASoCSASTParser: | ||||||
def get_scan_types(self): | ||||||
return ["HCL AppScan on Cloud SAST XML"] | ||||||
|
||||||
def get_label_for_scan_types(self, scan_type): | ||||||
return scan_type | ||||||
|
||||||
def get_description_for_scan_types(self, scan_type): | ||||||
return "Import XML output of HCL AppScan on Cloud SAST" | ||||||
|
||||||
def xmltreehelper(self, input): | ||||||
if input.text is None: | ||||||
output = None | ||||||
elif "\n" in input.text: | ||||||
output = "" | ||||||
for i in input: | ||||||
output = output + " " + i.text | ||||||
else: | ||||||
output = " " + input.text | ||||||
return output | ||||||
|
||||||
def get_findings(self, file, test): | ||||||
findings = [] | ||||||
tree = ET.parse(file) | ||||||
root = tree.getroot() | ||||||
if "xml-report" not in root.tag: | ||||||
msg = "This doesn't seem to be a valid HCL ASoC SAST xml file." | ||||||
raise NamespaceErr(msg) | ||||||
report = root.find("issue-group") | ||||||
if report is not None: | ||||||
for finding in report: | ||||||
title = "" | ||||||
description = "" | ||||||
for item in finding: | ||||||
match item.tag: | ||||||
case "severity": | ||||||
output = self.xmltreehelper(item) | ||||||
severity = "Info" if output is None else output.strip(" ").capitalize() | ||||||
case "cwe": | ||||||
cwe = int(self.xmltreehelper(item)) | ||||||
case "issue-type": | ||||||
title = self.xmltreehelper(item).strip() | ||||||
description = description + "**Issue-Type:** " + title + "\n" | ||||||
case "issue-type-name": | ||||||
title = self.xmltreehelper(item).strip() | ||||||
description = description + "**Issue-Type-Name:** " + title + "\n" | ||||||
case "source-file": | ||||||
location = self.xmltreehelper(item).strip() | ||||||
description = description + "**Location:** " + location + "\n" | ||||||
case "line": | ||||||
line = int(self.xmltreehelper(item).strip()) | ||||||
description = description + "***Line:" + str(line) + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
case "threat-class": | ||||||
threatclass = self.xmltreehelper(item) | ||||||
description = description + "***Threat-Class:" + threatclass + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
case "entity": | ||||||
entity = self.xmltreehelper(item) | ||||||
title += "_" + entity.strip() | ||||||
description = description + "***Entity:" + entity + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
case "security-risks": | ||||||
security_risks = self.xmltreehelper(item) | ||||||
description = description + "***Security-Risks:" + security_risks + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
case "cause-id": | ||||||
causeid = self.xmltreehelper(item) | ||||||
title += "_" + causeid.strip() | ||||||
description = description + "***Cause-Id:" + causeid + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
case "element": | ||||||
element = self.xmltreehelper(item) | ||||||
description = description + "***Element:" + element + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
case "element-type": | ||||||
elementtype = self.xmltreehelper(item) | ||||||
description = description + "***ElementType:" + elementtype + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
case "variant-group": | ||||||
variantgroup = item.iter() | ||||||
description = description + "***Call Trace:" + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
for vitem in variantgroup: | ||||||
if vitem.tag == "issue-information": | ||||||
issueinformation = vitem.iter() | ||||||
for iitem in issueinformation: | ||||||
if iitem.tag == "context": | ||||||
description = description + self.xmltreehelper(iitem) + "\n" | ||||||
|
||||||
case "fix": | ||||||
recommendations = "" | ||||||
externalreferences = "" | ||||||
issuetypename = "" | ||||||
remediation = "" | ||||||
fix = item.iter() | ||||||
for fitem in fix: | ||||||
if fitem.tag == "types": | ||||||
type = fitem.iter() | ||||||
for titem in type: | ||||||
if titem.tag == "name": | ||||||
issuetypename = self.xmltreehelper(titem) | ||||||
if fitem.tag == "remediation": | ||||||
remediation = self.xmltreehelper(fitem) | ||||||
|
||||||
articlegroup = root.find("article-group") | ||||||
if articlegroup is not None: | ||||||
for articles in articlegroup: | ||||||
if articles.attrib["id"] == issuetypename.strip() and articles.attrib["api"] == remediation.strip(): | ||||||
articledetails = articles.iter() | ||||||
for aitem in articledetails: | ||||||
if aitem.tag == "cause": | ||||||
description = description + "***Cause:" + "\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
for causeitem in aitem: | ||||||
if causeitem.attrib["type"] == "string" and causeitem.text is not None: | ||||||
description = description + causeitem.text + "\n" | ||||||
if aitem.tag == "recommendations": | ||||||
for recitem in aitem: | ||||||
if recitem.attrib["type"] == "string" and recitem.text is not None: | ||||||
recommendations = recommendations + recitem.text + "\n" | ||||||
elif recitem.attrib["type"] == "object": | ||||||
codeblock = recitem.iter() | ||||||
for codeitem in codeblock: | ||||||
if codeitem.tag == "item" and codeitem.attrib["type"] == "string": | ||||||
if codeitem.text is None: | ||||||
recommendations = recommendations + "\n" | ||||||
else: | ||||||
recommendations = recommendations + self.xmltreehelper(codeitem) + "\n" | ||||||
|
||||||
if aitem.tag == "externalReferences": | ||||||
ref = aitem.iter() | ||||||
for ritem in ref: | ||||||
if ritem.tag == "title": | ||||||
externalreferences = externalreferences + self.xmltreehelper(ritem).strip() + "\n" | ||||||
if ritem.tag == "url": | ||||||
externalreferences = externalreferences + self.xmltreehelper(ritem).strip() + "\n" | ||||||
|
||||||
prepared_finding = Finding( | ||||||
title=title, | ||||||
description=description, | ||||||
file_path=location, | ||||||
line=line, | ||||||
severity=severity, | ||||||
cwe=cwe, | ||||||
mitigation=recommendations, | ||||||
references=externalreferences, | ||||||
dynamic_finding=False, | ||||||
static_finding=True, | ||||||
) | ||||||
findings.append(prepared_finding) | ||||||
return findings | ||||||
return findings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of putting the whole function after this point inside an
if
block whenreport
is notNone
, just bail ifreport
isNone
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was keeping the overall style of the parser similar to the existing hcl_appscan (for DAST) parser for consistency. I can refactor if this is a dealbreaker.