Skip to content

Commit 57535c9

Browse files
authored
Update nancy parser.py
Nancy latest versions works with CVSS4.0 and "ID" instead of "Id",among others.
1 parent 0f9682b commit 57535c9

File tree

1 file changed

+79
-58
lines changed

1 file changed

+79
-58
lines changed

dojo/tools/nancy/parser.py

Lines changed: 79 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,107 @@
11
import json
2+
import re
23

3-
from cvss.cvss3 import CVSS3
4+
from cvss.cvss4 import CVSS4 # Changed from cvss3 to cvss4
45

56
from dojo.models import Finding
67

78

89
class NancyParser:
10+
911
def get_scan_types(self):
12+
1013
return ["Nancy Scan"]
1114

1215
def get_label_for_scan_types(self, scan_type):
16+
1317
return scan_type # no custom label for now
1418

1519
def get_description_for_scan_types(self, scan_type):
20+
1621
return ("Nancy output file (go list -json -deps ./... | nancy sleuth > "
1722
" nancy.json) can be imported in JSON format.")
1823

1924
def requires_file(self, scan_type):
20-
"""Return boolean indicating if parser requires a file to process."""
25+
2126
return True
2227

2328
def get_findings(self, scan_file, test):
24-
"""Return the collection of Findings ingested."""
25-
data = json.load(scan_file)
26-
findings = None
2729

28-
if "vulnerable" in data:
29-
findings = self.get_items(data["vulnerable"], test)
30-
else:
31-
msg = "Invalid format, unable to parse json."
30+
try:
31+
data = json.load(scan_file)
32+
except json.JSONDecodeError as e:
33+
msg = f"Invalid JSON format: {e}"
3234
raise ValueError(msg)
3335

34-
return findings
36+
if "vulnerable" in data and data["vulnerable"] is not None:
37+
return self.get_items(data["vulnerable"], test)
38+
else:
39+
# If 'vulnerable' is not present or is null, return empty list
40+
return []
41+
42+
def get_items(self, vulnerable_list, test):
3543

36-
def get_items(self, vulnerable, test):
3744
findings = []
38-
for vuln in vulnerable:
39-
finding = None
40-
severity = "Info"
41-
# the tool does not define severity, however it
42-
# provides CVSSv3 vector which will calculate
43-
# severity dynamically on save()
44-
references = []
45-
if vuln["Vulnerabilities"]:
46-
comp_name = vuln["Coordinates"].split(":")[1].split("@")[0]
47-
comp_version = vuln["Coordinates"].split(":")[1].split("@")[1]
48-
49-
references.append(vuln["Reference"])
50-
51-
for associated_vuln in vuln["Vulnerabilities"]:
52-
# create the finding object(s)
53-
references.append(associated_vuln["Reference"])
54-
vulnerability_ids = [associated_vuln["Cve"]]
55-
finding = Finding(
56-
title=associated_vuln["Title"],
57-
description=associated_vuln["Description"],
58-
test=test,
59-
severity=severity,
60-
component_name=comp_name,
61-
component_version=comp_version,
62-
false_p=False,
63-
duplicate=False,
64-
out_of_scope=False,
65-
static_finding=True,
66-
dynamic_finding=False,
67-
vuln_id_from_tool=associated_vuln["Id"],
68-
references="\n".join(references),
69-
)
70-
71-
finding.unsaved_vulnerability_ids = vulnerability_ids
72-
73-
# CVSSv3 vector
74-
if associated_vuln["CvssVector"]:
75-
finding.cvssv3 = CVSS3(
76-
associated_vuln["CvssVector"]).clean_vector()
77-
78-
# do we have a CWE?
79-
if associated_vuln["Title"].startswith("CWE-"):
80-
cwe = (associated_vuln["Title"]
81-
.split(":")[0].split("-")[1])
82-
finding.cwe = int(cwe)
83-
84-
findings.append(finding)
45+
for vuln in vulnerable_list:
46+
if not vuln.get("Vulnerabilities"):
47+
continue
48+
49+
try:
50+
coordinates = vuln["Coordinates"].split(":")
51+
comp_info = coordinates[1].split("@")
52+
comp_name = comp_info[0]
53+
comp_version = comp_info[1]
54+
except (IndexError, KeyError):
55+
# Handle cases where coordinate parsing might fail
56+
continue
57+
58+
for associated_vuln in vuln["Vulnerabilities"]:
59+
# The tool does not define severity, but it provides a CVSS vector,
60+
# which DefectDojo uses to calculate severity dynamically on save.
61+
severity = "Info"
62+
63+
# Aggregate references
64+
references = [vuln.get("Reference"), associated_vuln.get("Reference")]
65+
references = "\n".join(filter(None, references))
66+
67+
finding = Finding(
68+
title=associated_vuln.get("Title", "N/A"),
69+
description=associated_vuln.get("Description", "No description provided."),
70+
test=test,
71+
severity=severity,
72+
component_name=comp_name,
73+
component_version=comp_version,
74+
false_p=False,
75+
duplicate=False,
76+
out_of_scope=False,
77+
static_finding=True,
78+
dynamic_finding=False,
79+
vuln_id_from_tool=associated_vuln.get("ID"), # Changed from "Id" to "ID"
80+
references=references,
81+
)
82+
83+
# Set vulnerability IDs (e.g., CVEs)
84+
if cve := associated_vuln.get("Cve"):
85+
finding.unsaved_vulnerability_ids = [cve]
86+
87+
# Process CVSSv4 vector if available
88+
if cvss_vector := associated_vuln.get("CvssVector"):
89+
try:
90+
# Store the cleaned CVSSv4 vector. DefectDojo will process it.
91+
finding.cvssv3 = CVSS4(cvss_vector).clean_vector()
92+
except Exception:
93+
# Fallback or log error if vector is not valid CVSSv4
94+
pass # Or log a warning
95+
96+
# Extract CWE if present in the title using regex for robustness
97+
if title := associated_vuln.get("Title", ""):
98+
cwe_match = re.search(r"CWE-(\d+)", title, re.IGNORECASE)
99+
if cwe_match:
100+
try:
101+
finding.cwe = int(cwe_match.group(1))
102+
except (ValueError, IndexError):
103+
pass # Or log a warning
104+
105+
findings.append(finding)
85106

86107
return findings

0 commit comments

Comments
 (0)