From c3af5dc64d53dad5e1ad3049738a28f46e94ac13 Mon Sep 17 00:00:00 2001 From: dolevf Date: Mon, 5 Oct 2020 21:26:05 -0400 Subject: [PATCH] XML Report support --- core/reports.py | 42 ++++++++++++++++++++++++++++++++--- static/img/report_xml.png | Bin 0 -> 7022 bytes templates/documentation.html | 3 ++- templates/reports.html | 19 +++++++++++----- views/view_download.py | 11 ++++++++- 5 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 static/img/report_xml.png diff --git a/core/reports.py b/core/reports.py index e59d6c1..01c4d14 100644 --- a/core/reports.py +++ b/core/reports.py @@ -1,5 +1,6 @@ import csv import jinja2 +import xml.etree.ElementTree as xml from core.redis import rds from core.utils import Utils @@ -28,7 +29,6 @@ def generate_csv(data): return filename - def generate_html(vulns, conf): vuln_count = {0:0, 1:0, 2:0, 3:0, 4:0} filename = 'report-{}-{}.html'.format(utils.generate_uuid(), utils.get_date()) @@ -56,7 +56,6 @@ def generate_html(vulns, conf): return filename - def generate_txt(vulns): filename = 'report-{}-{}.txt'.format(utils.generate_uuid(), utils.get_date()) data = '' @@ -71,4 +70,41 @@ def generate_txt(vulns): return filename - \ No newline at end of file +def generate_xml(vulns): + filename = 'report-{}-{}.xml'.format(utils.generate_uuid(), utils.get_date()) + root = xml.Element("Vulnerabilities") + for key, value in vulns.items(): + vuln_element = xml.Element(key) + root.append(vuln_element) + + ip = xml.SubElement(vuln_element, "ip") + ip.text = value['ip'] + + port = xml.SubElement(vuln_element, "port") + port.text = str(value['port']) + + domain = xml.SubElement(vuln_element, "domain") + domain.text = value['domain'] + + sev = xml.SubElement(vuln_element, "severity") + sev.text = utils.sev_to_human(value['rule_sev']) + + description = xml.SubElement(vuln_element, "description") + description.text = value['rule_desc'] + + + confirm = xml.SubElement(vuln_element, "confirm") + confirm.text = value['rule_confirm'] + + details = xml.SubElement(vuln_element, "details") + details.text = value['rule_details'] + + mitigation = xml.SubElement(vuln_element, "mitigation") + mitigation.text = value['rule_mitigation'] + + data = xml.tostring(root) + f = open('reports/' + filename, "w") + f.write(data.decode('utf-8')) + f.close() + + return filename \ No newline at end of file diff --git a/static/img/report_xml.png b/static/img/report_xml.png new file mode 100644 index 0000000000000000000000000000000000000000..cd503b002e9edcaf6bdb02aca097b3284c260b4a GIT binary patch literal 7022 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 NX==TrcpEGFKLGPW?~nih literal 0 HcmV?d00001 diff --git a/templates/documentation.html b/templates/documentation.html index 0835a6e..6d2b121 100644 --- a/templates/documentation.html +++ b/templates/documentation.html @@ -265,7 +265,8 @@

Reports

  • TXT
  • CSV
  • - If you want to obtain the results of your assessment via the API, use the endpoint /api/scan/status +

    All the reports are saved on disk at /opt/nerve/reports if you need to go back in time and fetch historical reports.

    +

    If you want to obtain the results of your assessment via the API, use the endpoint /api/scan/status


    Notifications

    diff --git a/templates/reports.html b/templates/reports.html index 2881e72..a917115 100644 --- a/templates/reports.html +++ b/templates/reports.html @@ -62,35 +62,42 @@

    Reports

    +

    Below you can download your latest assessment report in various formats.

    -
    +
    +
    Detailed Report (HTML)
    -

    Report for the auditors

    -
    +
    Raw Report (TXT)
    -

    Report for the minimalists

    -
    +
    Raw Report (CSV)
    -

    Report for the nerds

    +
    +
    +
    Raw Report (XML)
    +
    +
    +
    +
    +
    diff --git a/views/view_download.py b/views/view_download.py index b402c6c..b3d3d7f 100644 --- a/views/view_download.py +++ b/views/view_download.py @@ -6,7 +6,8 @@ from core.reports import ( generate_html, generate_csv, - generate_txt + generate_txt, + generate_xml ) from flask import ( @@ -62,3 +63,11 @@ def view_download(file): as_attachment=True, cache_timeout=0) return response + + elif file == 'report_xml': + report_file = generate_xml(data) + response = send_from_directory(directory='reports', + filename=report_file, + as_attachment=True, + cache_timeout=0) + return response