From f9a9f81824354347263340ab5734d2647a246530 Mon Sep 17 00:00:00 2001 From: Scott Kuffer Date: Wed, 24 Feb 2021 08:41:08 -0500 Subject: [PATCH] Added support for trustwave pentest results --- trustwave/README.txt | 9 ++ trustwave/requirements.txt | 2 + trustwave/trustwave_pentest_xml.py | 208 +++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 trustwave/README.txt create mode 100644 trustwave/requirements.txt create mode 100644 trustwave/trustwave_pentest_xml.py diff --git a/trustwave/README.txt b/trustwave/README.txt new file mode 100644 index 0000000..e1f0c32 --- /dev/null +++ b/trustwave/README.txt @@ -0,0 +1,9 @@ +Script to pull the vulnerability report from a Trustwave automated pentest output, convert to Nucleus CSV, then post to Nucleus + *** Populate the following variables in the script prior to using *** + NUCLEUS_ROOT_URL + NUCLEUS_API_KEY + +Required command line arguments: + -i for input file path + -o for output file in Nucleus format + -# for project id \ No newline at end of file diff --git a/trustwave/requirements.txt b/trustwave/requirements.txt new file mode 100644 index 0000000..12bb43a --- /dev/null +++ b/trustwave/requirements.txt @@ -0,0 +1,2 @@ +requests +xmltodict \ No newline at end of file diff --git a/trustwave/trustwave_pentest_xml.py b/trustwave/trustwave_pentest_xml.py new file mode 100644 index 0000000..b5a11c7 --- /dev/null +++ b/trustwave/trustwave_pentest_xml.py @@ -0,0 +1,208 @@ +#!/usr/bin/python3.7 +__author__ = "Nucleus Security" +__license__ = "MIT License" +__version__ = "0.1" + +#Used for writing to csv +import csv +# Used for arguments +import argparse +# Used to post the file to Nucleus +import requests +import xmltodict +import json + + +# Enter in the root URL of your Nucleus instance. +# Example https://example.nucleussec.com +NUCLEUS_ROOT_URL = "{Enter root URL of your Nucleus Instance here}" + +# Generate an API key through the Nucleus UI +API_KEY = "{Enter your API key from Nucleus here}" + + +def convert_to_json(inputPath): + + with open (inputPath) as xml_file: + + data_dict = xmltodict.parse(xml_file.read()) + + xml_file.close() + + json_data = json.dumps(data_dict) + + #print(json_data) + + return json_data + + + +def customParser(json_data, outputPath): + + # Create the csv file for writing + with open(outputPath, 'w', newline='') as csvfile: + + csvwriter = csv.writer(csvfile, delimiter=',') + + csvwriter.writerow(['nucleus_import_version', 'host_name', 'ip_address', 'scan_type', 'scan_tool', 'finding_type', 'finding_cve', 'finding_number', 'finding_name', 'finding_severity', 'finding_description', 'finding_recommendation', 'finding_output', 'finding_result', 'finding_references']) + + # Try to parse the data. + try: + + json_data = json.loads(json_data) + + #print(json_data) + + export = json_data['export'] + + findings = export['finding'] + + #print(findings) + + for finding in findings: + + csv_line = [] + + severity = finding['severity'] + + finding_name = finding['title'] + + asset_name = finding['assetName'] + + asset_ip = finding['ip'] + + finding_number = finding['title'] + + description = finding['description'] + + scan_date = finding['createdOn'] + + # Loop and fix references + references = finding['references'] + + # Skipping references for now for speed + #TODO: Parse the references into Nucleus format + ''' + try: + + #reference_info = references.replace(",", ";") + + reference_info = references.replace(":", "-") + + reference_info = reference_info.replace(" ", "_") + + #Replace https:// + reference_info = reference_info.replace("ps-", "ps:") + + except: + + pass + + #print(reference_info) + + ''' + + + solution = finding['remediation'] + + # Loop and fix finding output + finding_output = finding['evidences']['evidence'] + + #print(finding_output) + + finding_output_data = '' + + try: + + for output in finding_output: + + title = output['title'] + value = output['value'] + output_type = output['type'] + + finding_output_data = output_type + ': ' + title + ' - ' + value + + #print(finding_output_data) + + except Exception as e: + + pass + + csv_line.extend(['1', asset_name, asset_ip, 'Host', 'Trustwave Pentest', 'Vuln', '', finding_number, finding_name, severity, description, solution, finding_output_data, 'Failed']) + + #print(csv_line) + + csvwriter.writerow(csv_line) + + + + except Exception as e: + + print("Error:", e) + + +def get_args(): + parser = argparse.ArgumentParser(description="For parsing whitesource files to be uploaded into Nucleus. If project ID is specified, will post the Nucleus supported file to Nucleus project.") + + # List arguments. Should only include input file and output file + parser.add_argument('-i', '--inputfile', dest='inputFile', help="Path to trustwave xml file", required=True) + parser.add_argument('-o', '--outputfile', dest='outputFile', help="Path to csv file output", required=True) + parser.add_argument('-#', '--project_id', dest="project_id", help="This is the project ID of the Nucleus project to which you want to post. If not specified, this script will only parse the whitesource file for manual upload.") + + # Define the arguments globally for ease of use + global args + + args = parser.parse_args() + + return args + +# Send the file to Nucleus +def post_to_nucleus(outputfile): + + # Enter the ID of the project which you wish to post to here + PROJECT_ID = args.project_id + + # open the file to send + with open(outputfile.name, 'rb') as f: + + # Get the final Nucleus URL to post to + nucleus_url = str(NUCLEUS_ROOT_URL+'/nucleus/api/projects/'+PROJECT_ID+'/scans') + + print("Posted to URL:", nucleus_url) + + # Send file with proper header. Keep note of the project ID you need to send + file_upload = requests.post(nucleus_url, files={outputfile.name: f}, headers={'x-apikey': API_KEY}) + + # Print the response from the server + print(file_upload.content) + + + +if __name__ == "__main__": + + # Get the arguments + arguments = get_args() + + # Get the input file to parse + inputPath = arguments.inputFile + + # Get the output file to save to + outputPath = arguments.outputFile + + json_data = convert_to_json(inputPath) + + # Start the parsing and csv writing + outputfile = customParser(json_data, outputPath) + + #print(outputfile.name) + + # If a project ID was specified, send the file to Nucleus + if arguments.project_id: + + # Send the newly created csv file to Nucleus if project id was specified + post_to_nucleus(outputfile) + + # If no project ID was specified, just parse file to Nucleus format for manual file upload + else: + + pass