Skip to content

Commit d2265c4

Browse files
h1: vulnerability disclosure parser improvements (#12212)
* h1: parse main_state * h1: parse main_state * h1: parse main_state * h1: parse main_state * h1: parse main_state
1 parent 115da96 commit d2265c4

4 files changed

Lines changed: 642 additions & 6 deletions

File tree

docs/content/en/connecting_your_tools/parsers/file/h1.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: "HackerOne Cases"
33
toc_hide: true
44
---
5-
Import HackerOne cases findings in JSON format
5+
Import HackerOne cases findings in JSON format (vulnerability disclosure parser) or Bug Bounties in JSON or CSV format (bug bounty parser)
66

77
### Sample Scan Data
88
Sample HackerOne Cases scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/h1).

dojo/tools/h1/parser.py

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
from datetime import datetime
77
from typing import ClassVar
88

9+
import cvss.parser
10+
from cvss.cvss3 import CVSS3
911
from dateutil import parser as date_parser
1012
from django.core.files.uploadedfile import TemporaryUploadedFile
13+
from django.utils import timezone
1114

1215
from dojo.models import Finding, Test
1316

@@ -49,14 +52,39 @@ def get_vulnerability_disclosure_json_findings(self, tree, test):
4952
severity = "Info"
5053
except Exception:
5154
severity = "Info"
55+
56+
# Try to grab CVSS fields
57+
if cvssv3_score := content.get("relationships", {}).get("severity", {}).get("data", {}).get("attributes", {}).get("score"):
58+
description += f"CVSS: {cvssv3_score}\n"
59+
60+
cvssv3_vector = None
61+
if cvss_vector_string := content.get("relationships", {}).get("severity", {}).get("data", {}).get("attributes", {}).get("cvss_vector_string"):
62+
vectors = cvss.parser.parse_cvss_from_text(cvss_vector_string)
63+
if len(vectors) > 0 and type(vectors[0]) is CVSS3:
64+
cvssv3_vector = vectors[0].clean_vector()
65+
if cvssv3_score is None:
66+
cvssv3_score = vectors[0].scores()[0]
67+
5268
# Build the references of the Dojo finding
5369
ref_link = "https://hackerone.com/reports/{}".format(
5470
content.get("id"),
5571
)
5672
references += f"[{ref_link}]({ref_link})"
5773

5874
# Set active state of the Dojo finding
59-
active = content["attributes"]["state"] in {"triaged", "new"}
75+
active = True
76+
if "main_state" in content["attributes"]:
77+
active = content["attributes"]["main_state"] != "closed"
78+
else:
79+
# If there is no main_state, we assume keep the old logic
80+
active = content["attributes"]["state"] in {"triaged", "new"}
81+
82+
is_mitigated = False
83+
mitigated = None
84+
if not active:
85+
is_mitigated = not active
86+
if is_mitigated:
87+
mitigated = date_parser.parse(content["attributes"]["closed_at"]) if content["attributes"].get("closed_at") else timezone.now()
6088

6189
# Set CWE of the Dojo finding
6290
try:
@@ -84,15 +112,24 @@ def get_vulnerability_disclosure_json_findings(self, tree, test):
84112
date=date,
85113
test=test,
86114
active=active,
115+
is_mitigated=is_mitigated,
116+
mitigated=mitigated,
87117
description=description,
88118
severity=severity,
89119
mitigation="See description",
90120
impact="No impact provided",
91121
references=references,
92122
cwe=cwe,
93123
dynamic_finding=False,
124+
cvssv3=cvssv3_vector,
125+
cvssv3_score=cvssv3_score,
94126
)
95127
finding.unsaved_endpoints = []
128+
129+
# Add vulnerability IDs if they are present
130+
if (cve_ids := content["attributes"].get("cve_ids")) is not None and len(cve_ids) > 0:
131+
finding.unsaved_vulnerability_ids = cve_ids
132+
96133
dupes[dupe_key] = finding
97134
return list(dupes.values())
98135

@@ -117,15 +154,30 @@ def build_description(self, content):
117154
)
118155
description += f"Triaged: {triaged_date}\n"
119156

120-
# Try to grab CVSS
121-
if cvss := content.get("relationships", {}).get("severity", {}).get("data", {}).get("attributes", {}).get("score"):
122-
description += f"CVSS: {cvss}\n"
123-
124157
# Build rest of description meat
125158
description += "##Report: \n{}\n".format(
126159
content["attributes"]["vulnerability_information"],
127160
)
128161

162+
structured_scope_fields_to_label: dict[str, str] = {
163+
"asset_identifier": "Asset Identifier",
164+
"asset_type": "Asset Type",
165+
"confidentiality_requirement": "Confidentiality Requirement",
166+
"integrity_requirement": "Integrity Requirement",
167+
"availability_requirement": "Availability Requirement",
168+
"max_severity": "Max Severity",
169+
"instruction": "Instruction",
170+
"eligible_for_bounty": "Eligible for Bounty",
171+
"eligible_for_submission": "Eligible for Submission",
172+
"reference": "Reference",
173+
}
174+
175+
if structured_scope_attributes := content.get("relationships", {}).get("structured_scope", {}).get("data", {}).get("attributes", {}):
176+
description += "\n##Structured Scope:\n"
177+
for field, label in structured_scope_fields_to_label.items():
178+
if (value := structured_scope_attributes.get(field)) is not None:
179+
description += f"**{label}**: {value}\n"
180+
129181
# Try to grab weakness if it's there
130182
if weakness_title := content.get("relationships", {}).get("weakness", {}).get("data", {}).get("attributes", {}).get("name"):
131183
if weakness_desc := content.get("relationships", {}).get("weakness", {}).get("data", {}).get("attributes", {}).get("description"):

0 commit comments

Comments
 (0)