Skip to content

Commit 90219c9

Browse files
authored
Merge pull request #1182 from madchap/sonatype-parser
Sonatype parser
2 parents 045ad95 + f195763 commit 90219c9

File tree

9 files changed

+542
-3
lines changed

9 files changed

+542
-3
lines changed

dojo/fixtures/test_type.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,5 +312,12 @@
312312
},
313313
"model": "dojo.test_type",
314314
"pk": 52
315+
},
316+
{
317+
"fields": {
318+
"name": "Sonatype Application Scan"
319+
},
320+
"model": "dojo.test_type",
321+
"pk": 53
315322
}
316323
]

dojo/forms.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ class ImportScanForm(forms.Form):
293293
("Bundler-Audit Scan", "Bundler-Audit Scan"),
294294
("Twistlock Image Scan", "Twistlock Image Scan"),
295295
("Kiuwan Scan", "Kiuwan Scan"),
296-
("Blackduck Hub Scan", "Blackduck Hub Scan"))
296+
("Blackduck Hub Scan", "Blackduck Hub Scan"),
297+
("Sonatype Application Scan", "Sonatype Application Scan"))
297298

298299
SORTED_SCAN_TYPE_CHOICES = sorted(SCAN_TYPE_CHOICES, key=lambda x: x[1])
299300

dojo/templates/dojo/import_scan_results.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ <h3> Add Tests</h3>
2626

2727
<p>DefectDojo accepts:</p>
2828
<ul>
29+
<li><b>Acunetix Scanner</b> - XML format.</li>
2930
<li><b>Anchore-Engine</b> - Anchore-CLI JSON vulnerability report format.</li>
3031
<li><b>AWS Scout2 Scanner</b> - JS file in scout2-report/inc-awsconfig/aws_config.js.</li>
3132
<li><b>AWS Prowler Scanner</b> - Prowler file can be imported as a CSV file (-M csv).</li>
@@ -46,6 +47,7 @@ <h3> Add Tests</h3>
4647
<li><b>Dependency Check</b> - OWASP Dependency Check output can be imported in Xml format.</li>
4748
<li><b>Generic Findings Import</b> - Import Generic findings in CSV format.</li>
4849
<li><b>Gosec Scanner </b> - Import Gosec Scanner findings in JSON format.</li>
50+
<li><b>Kiuwan Scanner</b> - Import Kiuwan Scan in CSV format. Export as CSV Results on Kiuwan.</li>
4951
<li><b>MobSF Scanner </b> - Export a JSON file using the API, api/v1/report_json.</li>
5052
<li><b>Nessus (Tenable)</b> - Reports can be imported as CSV or .nessus (XML) report formats.</li>
5153
<li><b>Netsparker Scanner</b> - Netsparker JSON format.</li>
@@ -64,6 +66,7 @@ <h3> Add Tests</h3>
6466
<li><b>SKF Scan</b> - Output of SKF Sprint summary export.</li>
6567
<li><b>Snyk</b> - Snyk output file (snyk test --json > snyk.json) can be imported in JSON format.</li>
6668
<!----<li><b>SonarQube</b> - SonarQube output file can be imported in HTML format.</li>-->
69+
<li><b>Sonatype Application Scan</b> - Can be imported in JSON format</li>
6770
<li><b>SpotBugs</b> - XML report of textui cli.</li>
6871
<li><b>SSL Labs</b> - JSON Output of ssllabs-scan cli.</li>
6972
<li><b>Trufflehog</b> - JSON Output of Trufflehog.</li>
@@ -72,8 +75,6 @@ <h3> Add Tests</h3>
7275
<li><b>Visual Code Grepper (VCG)</b> - VCG output can be imported in CSV or Xml formats.</li>
7376
<li><b>Veracode Detailed XML Report</b></li>
7477
<li><b>Zed Attack Proxy</b> - ZAP XML report format.</li>
75-
<li><b>Acunetix Scanner</b> - XML format.</li>
76-
<li><b>Kiuwan Scanner</b> - Import Kiuwan Scan in CSV format. Export as CSV Results on Kiuwan.</li>
7778

7879
</ul>
7980

dojo/tools/factory.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from dojo.tools.twistlock.parser import TwistlockParser
4747
from dojo.tools.kiuwan.parser import KiuwanCSVParser
4848
from dojo.tools.blackduck.parser import BlackduckHubCSVParser
49+
from dojo.tools.sonatype.parser import SonatypeJSONParser
4950

5051
__author__ = 'Jay Paz'
5152

@@ -153,6 +154,8 @@ def import_parser_factory(file, test, scan_type=None):
153154
parser = KiuwanCSVParser(file, test)
154155
elif scan_type == 'Blackduck Hub Scan':
155156
parser = BlackduckHubCSVParser(file, test)
157+
elif scan_type == 'Sonatype Application Scan':
158+
parser = SonatypeJSONParser(file, test)
156159
else:
157160
raise ValueError('Unknown Test Type')
158161

dojo/tools/sonatype/__init__.py

Whitespace-only changes.

dojo/tools/sonatype/parser.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
""" Surely a fragile parser, but gets things started and will evolve over time I guess.
2+
It seems that a lot of the json data does not have any securityData data
3+
So these are just skipped, since there is nothing much to do with them here
4+
"""
5+
import json
6+
from dojo.models import Finding
7+
8+
9+
class SonatypeJSONParser(object):
10+
# This parser does not deal with licenses information.
11+
def __init__(self, json_output, test):
12+
tree = self.parse_json(json_output)
13+
14+
if tree:
15+
self.items = [data for data in self.get_items(tree, test)]
16+
else:
17+
self.items = []
18+
19+
def parse_json(self, json_output):
20+
try:
21+
tree = json.load(json_output)
22+
except:
23+
raise Exception("Invalid format")
24+
25+
return tree
26+
27+
def get_items(self, tree, test):
28+
items = {}
29+
if 'components' in tree:
30+
vulnerability_tree = tree['components']
31+
32+
for node in vulnerability_tree:
33+
item = get_item(node, test)
34+
if item is None:
35+
continue
36+
# TODO
37+
unique_key = node['hash']
38+
items[unique_key] = item
39+
40+
return items.values()
41+
42+
43+
def get_item(vulnerability, test):
44+
# Following the CVSS Scoring per https://nvd.nist.gov/vuln-metrics/cvss
45+
if vulnerability['securityData'] is not None and len(vulnerability['securityData']['securityIssues']) >= 1:
46+
# there can be nothing in the array, or securityData can be null altogether. If the latter, well, nothing much to do?
47+
# issues is an array, and there can be 2+ of them, e.g. a cve and a sonatype entry or two cves
48+
# Given the current Finding class, if a cve, will be the main. If not a cve, then CVE ref will remain null due to regex.
49+
# Others go to references.
50+
main_finding = vulnerability['securityData']['securityIssues'][0]
51+
52+
if main_finding.get("source") == "cve":
53+
cve = main_finding.get("reference")
54+
else:
55+
# if sonatype of else, will not match Finding model today
56+
cve = ""
57+
58+
if main_finding['severity'] <= 3.9:
59+
severity = "Low"
60+
elif main_finding['severity'] > 4.0 and main_finding['severity'] <= 6.9:
61+
severity = "Medium"
62+
elif main_finding['severity'] and main_finding['severity'] <= 8.9:
63+
severity = "High"
64+
else:
65+
severity = "Critical"
66+
67+
references = []
68+
if len(vulnerability['securityData']['securityIssues']) > 1:
69+
for additional_issue in vulnerability['securityData']['securityIssues']:
70+
references.append("{}, {}, {}, {}, {} ".format(
71+
additional_issue.get("reference"),
72+
additional_issue.get("status"),
73+
additional_issue.get("severity"),
74+
additional_issue.get("threatCategory"),
75+
additional_issue.get("url"))
76+
)
77+
78+
component_id = ''
79+
if 'componentIdentifier' in vulnerability:
80+
if vulnerability['componentIdentifier']['format'] == "maven":
81+
component_id = "{} {} {}".format(
82+
vulnerability['componentIdentifier']['coordinates']['artifactId'],
83+
vulnerability['componentIdentifier']['coordinates']['groupId'],
84+
vulnerability['componentIdentifier']['coordinates']['version']
85+
)
86+
elif vulnerability['componentIdentifier']['format'] == "a-name":
87+
component_id = "{} {} {}".format(
88+
vulnerability['componentIdentifier']['coordinates']['name'],
89+
vulnerability['componentIdentifier']['coordinates']['qualifier'],
90+
vulnerability['componentIdentifier']['coordinates']['version']
91+
)
92+
93+
finding_title = "{} - {}".format(
94+
main_finding['reference'],
95+
component_id
96+
)
97+
98+
finding_description = "Hash {}\n\n".format(vulnerability['hash'])
99+
finding_description += component_id
100+
finding_description += "\n\nPlease check the CVE details for a detailed description. If a sonatype issue, you're out of luck."
101+
threat_category = main_finding.get("threatCategory", "CVSS vector not provided. ").title()
102+
status = main_finding['status']
103+
score = main_finding.get('severity', "No CVSS score yet.")
104+
if 'pathnames' in vulnerability:
105+
file_path = ' '.join(vulnerability['pathnames'])
106+
else:
107+
file_path = ''
108+
109+
# create the finding object
110+
finding = Finding(
111+
title=finding_title,
112+
cve=cve,
113+
test=test,
114+
severity=severity,
115+
numerical_severity=Finding.get_numerical_severity(severity),
116+
description=finding_description,
117+
mitigation=status,
118+
references="{}\n{}\n".format(main_finding['url'], "\n".join(references)),
119+
active=False,
120+
verified=False,
121+
false_p=False,
122+
duplicate=False,
123+
out_of_scope=False,
124+
mitigated=None,
125+
file_path=file_path,
126+
impact=threat_category,
127+
static_finding=True
128+
)
129+
130+
finding.description = finding.description.strip()
131+
132+
return finding
133+
else:
134+
return None

0 commit comments

Comments
 (0)