Skip to content

Commit 228d559

Browse files
authored
Merge branch 'dev' into add_kiuwan_support
2 parents 016582d + 7b2d680 commit 228d559

File tree

10 files changed

+166
-2
lines changed

10 files changed

+166
-2
lines changed

dojo/forms.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ class ImportScanForm(forms.Form):
291291
("Anchore Engine Scan", "Anchore Engine Scan"),
292292
("Bundler-Audit Scan", "Bundler-Audit Scan"),
293293
("Twistlock Image Scan", "Twistlock Image Scan"),
294-
("Kiuwan Scan", "Kiuwan Scan"))
294+
("Kiuwan Scan", "Kiuwan Scan"),
295+
("Blackduck Hub Scan", "Blackduck Hub Scan"))
295296

296297
SORTED_SCAN_TYPE_CHOICES = sorted(SCAN_TYPE_CHOICES, key=lambda x: x[1])
297298

dojo/templates/dojo/import_scan_results.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ <h3> Add Tests</h3>
3232
<li><b>Arachni Scanner</b> - Arachni JSON report format.</li>
3333
<li><b>AppSpider (Rapid7)</b> - Use the VulnerabilitiesSummary.xml file found in the zipped report download.</li>
3434
<li><b>Bandit</b> - JSON report format</li>
35+
<li><b>Blackduck Hub</b> - CSV report format (security.csv)</li>
3536
<li><b>Bundler-Audit Scan</b> - 'bundler-audit check' output (in plain text)</li>
3637
<li><b>Burp XML</b> - When the Burp report is generated, the recommended option is Base64 encoding both the request and
3738
response fields. These fields will be processed and made available in the 'Finding View' page.</li>

dojo/tools/blackduck/__init__.py

Whitespace-only changes.

dojo/tools/blackduck/parser.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import pandas as pd
2+
import hashlib
3+
from dojo.models import Finding
4+
5+
6+
class BlackduckHubCSVParser(object):
7+
"""
8+
security.csv fields
9+
1 project id -- ignore
10+
2 version id -- ignore
11+
3 chan version id -- ignore
12+
4 Project name
13+
5 Version NO -- part of channel id
14+
6 channel version origin (i.e maven)
15+
7 Channel version origin id YES
16+
8 channel version origin name NO, part of ID already
17+
9 Vulnerability id (either a CVE or some random number from VULNDB?)
18+
10 Description
19+
11 Published on
20+
12 Updated on
21+
13 Base score
22+
14 Exploitability
23+
15 Impact
24+
16 Vulnerability source
25+
17 Remediation status (NEW, DUPLICATE...)
26+
18 Remediation target date
27+
19 Remediation actual date
28+
20 Remediation comment
29+
21 URL (can be empty)
30+
22 Security Risk
31+
"""
32+
def __init__(self, filename, test):
33+
dupes = dict()
34+
self.items = ()
35+
36+
if filename is None:
37+
self.items = ()
38+
return
39+
40+
df = pd.read_csv(filename, header=0)
41+
df = df.fillna("N/A")
42+
43+
for i, row in df.iterrows():
44+
cve = df.ix[i, 'Vulnerability id']
45+
cwe = 0 # need a way to automaticall retrieve that see #1119
46+
title = self.format_title(df, i)
47+
description = self.format_description(df, i)
48+
severity = str(df.ix[i, 'Security Risk']).title()
49+
mitigation = self.format_mitigation(df, i)
50+
impact = df.ix[i, 'Impact']
51+
references = self.format_reference(df, i)
52+
53+
dupe_key = hashlib.md5(title + '|' + df.ix[i, 'Vulnerability source']).hexdigest()
54+
55+
if dupe_key in dupes:
56+
finding = dupes[dupe_key]
57+
if finding.description:
58+
finding.description += "Vulnerability ID: {}\n {}\n".format(
59+
df.ix[i, 'Vulnerability id'], df.ix[i, 'Vulnerability source'])
60+
dupes[dupe_key] = finding
61+
else:
62+
dupes[dupe_key] = True
63+
64+
finding = Finding(title=title,
65+
cwe=int(cwe),
66+
test=test,
67+
active=False,
68+
verified=False,
69+
description=description,
70+
severity=severity,
71+
numerical_severity=Finding.get_numerical_severity(
72+
severity),
73+
mitigation=mitigation,
74+
impact=impact,
75+
references=references,
76+
url=df.ix[i, 'URL'],
77+
dynamic_finding=True)
78+
79+
dupes[dupe_key] = finding
80+
81+
self.items = dupes.values()
82+
83+
def format_title(self, df, i):
84+
return "{} - {}".format(df.ix[i, 'Vulnerability id'], df.ix[i, 'Channel version origin id'])
85+
86+
def format_description(self, df, i):
87+
description = "Published on: {}\n\n".format(str(df.ix[i, 'Published on']))
88+
description += "Updated on: {}\n\n".format(str(df.ix[i, 'Updated on']))
89+
description += "Base score: {}\n\n".format(str(df.ix[i, 'Base score']))
90+
description += "Exploitability: {}\n\n".format(str(df.ix[i, 'Exploitability']))
91+
description += "Description: {}\n".format(df.ix[i, 'Description'])
92+
93+
return description
94+
95+
def format_mitigation(self, df, i):
96+
mitigation = "Remediation status: {}\n".format(df.ix[i, 'Remediation status'])
97+
mitigation += "Remediation target date: {}\n".format(df.ix[i, 'Remediation target date'])
98+
mitigation += "Remdediation actual date: {}\n".format(df.ix[i, 'Remediation actual date'])
99+
mitigation += "Remdediation comment: {}\n".format(df.ix[i, 'Remediation comment'])
100+
101+
return mitigation
102+
103+
def format_reference(self, df, i):
104+
reference = "Source: {}\n".format(df.ix[i, 'Vulnerability source'])
105+
reference += "URL: {}\n".format(df.ix[i, 'URL'])
106+
107+
return reference

dojo/tools/factory.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from dojo.tools.bundler_audit.parser import BundlerAuditParser
4545
from dojo.tools.twistlock.parser import TwistlockParser
4646
from dojo.tools.kiuwan.parser import KiuwanCSVParser
47+
from dojo.tools.blackduck.parser import BlackduckHubCSVParser
4748

4849
__author__ = 'Jay Paz'
4950

@@ -147,6 +148,8 @@ def import_parser_factory(file, test, scan_type=None):
147148
parser = TwistlockParser(file, test)
148149
elif scan_type == 'Kiuwan Scan':
149150
parser = KiuwanCSVParser(file, test)
151+
elif scan_type == 'Blackduck Hub Scan':
152+
parser = BlackduckHubCSVParser(file, test)
150153
else:
151154
raise ValueError('Unknown Test Type')
152155

dojo/tools/twistlock/parser.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ def get_item(vulnerability, test):
5454
vector = vulnerability['vector'] if 'vector' in vulnerability else "CVSS vector not provided. "
5555
status = vulnerability['status'] if 'status' in vulnerability else "There seems to be no fix yet. Please check description field."
5656
cvss = vulnerability['cvss'] if 'cvss' in vulnerability else "No CVSS score yet."
57+
riskFactors = vulnerability['riskFactors'] if 'riskFactors' in vulnerability else "No risk factors."
5758

5859
# create the finding object
5960
finding = Finding(
6061
title=vulnerability['id'] + ": " + vulnerability['packageName'] + " - " + vulnerability['packageVersion'],
62+
cve=vulnerability['id'],
6163
test=test,
6264
severity=severity,
6365
description=vulnerability['description'] + "<p> Vulnerable Package: " +
@@ -71,7 +73,7 @@ def get_item(vulnerability, test):
7173
duplicate=False,
7274
out_of_scope=False,
7375
mitigated=None,
74-
severity_justification="{}({})\n\n{}".format(vector, cvss, vulnerability['riskFactors']),
76+
severity_justification="{}({})\n\n{}".format(vector, cvss, riskFactors),
7577
impact=severity)
7678

7779
finding.description = finding.description.strip()

0 commit comments

Comments
 (0)