forked from DefectDojo/django-DefectDojo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🔨 Refactor nikto to separate json and xml (DefectDojo#9680)
* basic structure * json part * xml part * flake8
- Loading branch information
1 parent
d9e38e6
commit d389a38
Showing
3 changed files
with
139 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import json | ||
from dojo.models import Endpoint, Finding | ||
|
||
|
||
class NiktoJSONParser(object): | ||
def process_json(self, file, test): | ||
data = json.load(file) | ||
if len(data) == 1 and isinstance(data, list): | ||
data = data[0] | ||
dupes = dict() | ||
host = data.get("host") | ||
port = data.get("port") | ||
if port is not None: | ||
port = int(port) | ||
for vulnerability in data.get("vulnerabilities", []): | ||
description = "\n".join([ | ||
f"**id:** `{vulnerability.get('id')}`", | ||
f"**msg:** `{vulnerability.get('msg')}`", | ||
f"**HTTP Method:** `{vulnerability.get('method')}`", | ||
]) | ||
if vulnerability.get('OSVDB') is not None: | ||
description += "\n" + f"**OSVDB:** `{vulnerability.get('OSVDB')}`" | ||
finding = Finding( | ||
title=vulnerability.get("msg"), | ||
severity="Info", # Nikto doesn't assign severity, default to Info | ||
description=description, | ||
vuln_id_from_tool=vulnerability.get("id"), | ||
nb_occurences=1, | ||
references=vulnerability.get("references") | ||
) | ||
# manage if we have an ID from OSVDB | ||
if "OSVDB" in vulnerability and "0" != vulnerability.get("OSVDB"): | ||
finding.unique_id_from_tool = "OSVDB-" + vulnerability.get( | ||
"OSVDB" | ||
) | ||
finding.description += "\n*This finding is marked as medium as there is a link to OSVDB*" | ||
finding.severity = "Medium" | ||
# build the endpoint | ||
endpoint = Endpoint( | ||
host=host, | ||
port=port, | ||
path=vulnerability.get("url"), | ||
) | ||
finding.unsaved_endpoints = [endpoint] | ||
# internal de-duplication | ||
dupe_key = finding.severity + finding.title | ||
if dupe_key in dupes: | ||
find = dupes[dupe_key] | ||
find.description += "\n-----\n" + finding.description | ||
find.unsaved_endpoints.append(endpoint) | ||
find.unique_id_from_tool = ( | ||
None # as it is an aggregated finding we erase ids | ||
) | ||
find.vuln_id_from_tool = ( | ||
None # as it is an aggregated finding we erase ids | ||
) | ||
find.nb_occurences += 1 | ||
else: | ||
dupes[dupe_key] = finding | ||
return list(dupes.values()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import re | ||
import hashlib | ||
import logging | ||
from dojo.models import Endpoint, Finding | ||
from defusedxml import ElementTree as ET | ||
from django.core.exceptions import ValidationError | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class NiktoXMLParser(object): | ||
def process_xml(self, file, test): | ||
dupes = dict() | ||
tree = ET.parse(file) | ||
root = tree.getroot() | ||
scan = root.find("scandetails") | ||
if scan is not None: | ||
self.process_scandetail(scan, test, dupes) | ||
else: | ||
# New versions of Nikto have a new file type (nxvmlversion="1.2") which adds an additional niktoscan tag | ||
# This find statement below is to support new file format while not | ||
# breaking older Nikto scan files versions. | ||
for scan in root.findall("./niktoscan/scandetails"): | ||
self.process_scandetail(scan, test, dupes) | ||
return list(dupes.values()) | ||
|
||
def process_scandetail(self, scan, test, dupes): | ||
for item in scan.findall("item"): | ||
# Title | ||
titleText = None | ||
description = item.findtext("description") | ||
# Cut the title down to the first sentence | ||
sentences = re.split( | ||
r"(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)\s", description | ||
) | ||
if len(sentences) > 0: | ||
titleText = sentences[0][:900] | ||
else: | ||
titleText = description[:900] | ||
# Description | ||
description = "\n".join( | ||
[ | ||
f"**Host:** `{item.findtext('iplink')}`", | ||
f"**Description:** `{item.findtext('description')}`", | ||
f"**HTTP Method:** `{item.attrib.get('method')}`", | ||
] | ||
) | ||
# Manage severity the same way with JSON | ||
severity = "Info" # Nikto doesn't assign severity, default to Info | ||
if item.get("osvdbid") is not None and "0" != item.get("osvdbid"): | ||
severity = "Medium" | ||
finding = Finding( | ||
title=titleText, | ||
test=test, | ||
description=description, | ||
severity=severity, | ||
dynamic_finding=True, | ||
static_finding=False, | ||
vuln_id_from_tool=item.attrib.get("id"), | ||
nb_occurences=1, | ||
) | ||
# endpoint | ||
try: | ||
ip = item.findtext("iplink") | ||
endpoint = Endpoint.from_uri(ip) | ||
finding.unsaved_endpoints = [endpoint] | ||
except ValidationError: | ||
logger.debug("Invalid iplink in the report") | ||
dupe_key = hashlib.sha256(description.encode("utf-8")).hexdigest() | ||
if dupe_key in dupes: | ||
find = dupes[dupe_key] | ||
find.description += "\n-----\n" + finding.description | ||
find.unsaved_endpoints.extend(finding.unsaved_endpoints) | ||
find.nb_occurences += 1 | ||
else: | ||
dupes[dupe_key] = finding |