Skip to content

Commit 72a1b8d

Browse files
authored
Add support for GitLab Dependency Scanning reports (#3534)
1 parent 0328eb9 commit 72a1b8d

File tree

10 files changed

+942
-1
lines changed

10 files changed

+942
-1
lines changed

dojo/fixtures/test_type.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,5 +684,13 @@
684684
},
685685
"model": "dojo.test_type",
686686
"pk": 189
687+
},
688+
{
689+
"fields": {
690+
"name": "GitLab Dependency Scanning Report"
691+
},
692+
"model": "dojo.test_type",
693+
"pk": 190
687694
}
695+
688696
]

dojo/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ class ImportScanForm(forms.Form):
402402
("BugCrowd Scan", "BugCrowd Scan"),
403403
("GitLab SAST Report", "GitLab SAST Report"),
404404
("AWS Security Hub Scan", "AWS Security Hub Scan"),
405-
("GitLab SAST Report", "GitLab SAST Report"),
405+
("GitLab Dependency Scanning Report", "GitLab Dependency Scanning Report"),
406406
("HuskyCI Report", "HuskyCI Report"),
407407
("Semgrep JSON Report", "Semgrep JSON Report"),
408408
("Risk Recon API Importer", "Risk Recon API Importer"),

dojo/templates/dojo/import_scan_results.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ <h4> JIRA </h4>
8282
<li><b>Fortify</b> - Import Findings from XML file format.</li>
8383
<li><b>Generic Findings Import</b> - Import Generic findings in CSV format.</li>
8484
<li><b>GitLab SAST Report</b> - Import GitLab SAST Report vulnerabilities in JSON format.</li>
85+
<li><b>GitLab Dependency Scanning Report</b> - Import GitLab Dependency Scanning Report vulnerabilities in JSON format.</li>
8586
<li><b>Gitleaks Scan </b> - Import Gitleaks Scan findings in JSON format.</li>
8687
<li><b>Gosec Scanner </b> - Import Gosec Scanner findings in JSON format.</li>
8788
<li><b>HackerOne Cases</b> - Import HackerOne cases findings in JSON format.</li>

dojo/tools/factory.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
from dojo.tools.github_vulnerability.parser import GithubVulnerabilityParser
8383
from dojo.tools.choctaw_hog.parser import ChoctawhogParser
8484
from dojo.tools.gitlab_sast.parser import GitlabSastReportParser
85+
from dojo.tools.gitlab_dep_scan.parser import GitlabDepScanReportParser
8586
from dojo.tools.yarn_audit.parser import YarnAuditParser
8687
from dojo.tools.bugcrowd.parser import BugCrowdCSVParser
8788
from dojo.tools.huskyci.parser import HuskyCIReportParser
@@ -277,6 +278,8 @@ def import_parser_factory(file, test, active, verified, scan_type=None):
277278
parser = ChoctawhogParser(file, test)
278279
elif scan_type == 'GitLab SAST Report':
279280
parser = GitlabSastReportParser(file, test)
281+
elif scan_type == 'GitLab Dependency Scanning Report':
282+
parser = GitlabDepScanReportParser(file, test)
280283
elif scan_type == 'Yarn Audit Scan':
281284
parser = YarnAuditParser(file, test)
282285
elif scan_type == 'BugCrowd Scan':

dojo/tools/gitlab_dep_scan/__init__.py

Whitespace-only changes.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import json
2+
from dojo.models import Finding
3+
4+
5+
class GitlabDepScanReportParser(object):
6+
def __init__(self, json_output, test):
7+
self.items = []
8+
9+
if json_output is None:
10+
return
11+
12+
tree = self.parse_json(json_output)
13+
if tree:
14+
self.items = [data for data in self.get_items(tree, test)]
15+
16+
def parse_json(self, json_output):
17+
try:
18+
data = json_output.read()
19+
try:
20+
tree = json.loads(str(data, 'utf-8'))
21+
except:
22+
tree = json.loads(data)
23+
except:
24+
raise Exception("Invalid format")
25+
26+
return tree
27+
28+
def get_items(self, tree, test):
29+
items = {}
30+
31+
for node in tree['vulnerabilities']:
32+
item = get_item(node, test)
33+
if item:
34+
items[item.unique_id_from_tool] = item
35+
36+
return list(items.values())
37+
38+
39+
def get_item(vuln, test):
40+
if vuln['category'] != 'dependency_scanning':
41+
# For Dependency Scanning reports, value must always be "dependency_scanning"
42+
return None
43+
44+
unique_id_from_tool = None
45+
if 'id' in vuln:
46+
unique_id_from_tool = vuln['id']
47+
else:
48+
# If the new unique id is not provided, fall back to deprecated "cve" fingerprint (old version)
49+
unique_id_from_tool = vuln['cve']
50+
51+
title = ''
52+
if 'name' in vuln:
53+
title = vuln['name']
54+
elif 'message' in vuln:
55+
title = vuln['message']
56+
elif 'description' in vuln:
57+
title = vuln['description']
58+
else:
59+
# All other fields are optional, if none of them has a value, fall back on the unique id
60+
title = unique_id_from_tool
61+
62+
description = 'Scanner: {}\n'.format(vuln['scanner']['name'])
63+
if 'message' in vuln:
64+
description += '{}\n'.format(vuln['message'])
65+
if 'description' in vuln:
66+
description += '{}\n'.format(vuln['description'])
67+
68+
location = vuln['location']
69+
file_path = location['file'] if 'file' in location else None
70+
sourcefile = location['file'] if 'file' in location else None
71+
72+
line = location['start_line'] if 'start_line' in location else None
73+
if 'end_line' in location:
74+
line = location['end_line']
75+
76+
sast_source_line = location['start_line'] if 'start_line' in location else None
77+
78+
sast_object = None
79+
if 'class' in location and 'method' in location:
80+
sast_object = '{}#{}'.format(location['class'], location['method'])
81+
elif 'class' in location:
82+
sast_object = location['class']
83+
elif 'method' in location:
84+
sast_object = location['method']
85+
86+
severity = vuln['severity']
87+
if severity == 'Undefined' or severity == 'Unknown':
88+
# Severity can be "Undefined" or "Unknown" in report
89+
# In that case we set it as Info and specify the initial severity in the title
90+
title = '[{} severity] {}'.format(severity, title)
91+
severity = 'Info'
92+
numerical_severity = Finding.get_numerical_severity(severity)
93+
# Dependency Scanning analyzers doesn't provide confidence property
94+
# See https://docs.gitlab.com/ee/user/application_security/dependency_scanning/analyzers.html#analyzers-data
95+
scanner_confidence = False
96+
97+
mitigation = ''
98+
if 'solution' in vuln:
99+
mitigation = vuln['solution']
100+
101+
cwe = None
102+
cve = None
103+
references = ''
104+
if 'identifiers' in vuln:
105+
for identifier in vuln['identifiers']:
106+
if identifier['type'].lower() == 'cwe':
107+
cwe = identifier['value']
108+
elif identifier['type'].lower() == 'cve':
109+
cve = identifier['value']
110+
else:
111+
references += 'Identifier type: {}\n'.format(identifier['type'])
112+
references += 'Name: {}\n'.format(identifier['name'])
113+
references += 'Value: {}\n'.format(identifier['value'])
114+
if 'url' in identifier:
115+
references += 'URL: {}\n'.format(identifier['url'])
116+
references += '\n'
117+
118+
finding = Finding(title=cve + ": " + title if cve else title,
119+
test=test,
120+
active=False,
121+
verified=False,
122+
description=description,
123+
severity=severity,
124+
numerical_severity=numerical_severity,
125+
scanner_confidence=scanner_confidence,
126+
mitigation=mitigation,
127+
unique_id_from_tool=unique_id_from_tool,
128+
references=references,
129+
file_path=file_path,
130+
sourcefile=sourcefile,
131+
line=line,
132+
sast_source_object=sast_object,
133+
sast_sink_object=sast_object,
134+
sast_source_file_path=file_path,
135+
sast_source_line=sast_source_line,
136+
cwe=cwe,
137+
cve=cve,
138+
static_finding=True,
139+
dynamic_finding=False)
140+
141+
return finding
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"version": "3.0.0",
3+
"vulnerabilities": [],
4+
"remediations": [],
5+
"dependency_files": [
6+
{
7+
"path": "service/go.sum",
8+
"package_manager": "go",
9+
"dependencies": []
10+
}
11+
],
12+
"scan": {
13+
"scanner": {
14+
"id": "gemnasium",
15+
"name": "Gemnasium",
16+
"url": "https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium",
17+
"vendor": {
18+
"name": "GitLab"
19+
},
20+
"version": "2.24.1"
21+
},
22+
"type": "dependency_scanning",
23+
"start_time": "2020-12-23T13:44:07",
24+
"end_time": "2020-12-23T13:44:08",
25+
"status": "success"
26+
}
27+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"version": "3.0.0",
3+
"vulnerabilities": [
4+
{
5+
"id": "2d8b607cb56d9866c73cdcf33a016f64b4fa37d909c1dd300037b1ac026a3ca5",
6+
"category": "dependency_scanning",
7+
"name": "XML Entity Expansion",
8+
"message": "XML Entity Expansion in gopkg.in/yaml.v2",
9+
"description": "go-yaml is vulnerable to a Billion Laughs Attack.",
10+
"cve": "service/go.sum:gopkg.in/yaml.v2:gemnasium:7368f513-0aa9-4e34-a08d-40ea81f48e0e",
11+
"severity": "Unknown",
12+
"solution": "Upgrade to version 2.2.3 or above.",
13+
"scanner": {
14+
"id": "gemnasium",
15+
"name": "Gemnasium"
16+
},
17+
"location": {
18+
"file": "service/go.sum",
19+
"dependency": {
20+
"package": {
21+
"name": "gopkg.in/yaml.v2"
22+
},
23+
"version": "v2.2.2"
24+
}
25+
},
26+
"identifiers": [
27+
{
28+
"type": "gemnasium",
29+
"name": "Gemnasium-7368f513-0aa9-4e34-a08d-40ea81f48e0e",
30+
"value": "7368f513-0aa9-4e34-a08d-40ea81f48e0e",
31+
"url": "https://gitlab.com/gitlab-org/security-products/gemnasium-db/-/blob/master/go/gopkg.in/yaml.v2/GMS-2019-2.yml"
32+
}
33+
],
34+
"links": [
35+
{
36+
"url": "https://github.com/docker/cli/pull/2117"
37+
}
38+
]
39+
}
40+
],
41+
"remediations": [],
42+
"dependency_files": [
43+
{
44+
"path": "service/go.sum",
45+
"package_manager": "go",
46+
"dependencies": [
47+
{
48+
"package": {
49+
"name": "gopkg.in/yaml.v2"
50+
},
51+
"version": "v2.2.2"
52+
},
53+
{
54+
"package": {
55+
"name": "gopkg.in/yaml.v2"
56+
},
57+
"version": "v2.2.4"
58+
}
59+
]
60+
}
61+
],
62+
"scan": {
63+
"scanner": {
64+
"id": "gemnasium",
65+
"name": "Gemnasium",
66+
"url": "https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium",
67+
"vendor": {
68+
"name": "GitLab"
69+
},
70+
"version": "2.24.1"
71+
},
72+
"type": "dependency_scanning",
73+
"start_time": "2020-12-23T13:43:48",
74+
"end_time": "2020-12-23T13:43:49",
75+
"status": "success"
76+
}
77+
}

0 commit comments

Comments
 (0)