#!/usr/bin/env python
import json
import colorama
import sys
import re
from termcolor import colored
from tabulate import tabulate

'''
Quick script to format the result of a OWASP Dependency Check (ODC) JSON report:
    dependency-check.sh --project MyProject --scan . --format JSON --prettyPrint --out ./odc.json
    python generate-report-odc.py odc.json

A CSV file can be generated too with the list of printed vulnerabilities, just add the "--csv" option at the end of the command.
    python generate-report-odc.py odc.json --csv

Dependencies:
    pip install tabulate colorama termcolor
'''
odc_json_report = sys.argv[1]
generate_csv_file_for_vulns = ((len(sys.argv) == 3) and (sys.argv[2].lower() == "--csv"))
csv_separator_char = ";"
colorama.init()
severity_no_color_regex = r'(CRITICAL|HIGH|MEDIUM|LOW|UNKNOWN|NONE|MODERATE)'
with open(odc_json_report) as f:
    content = f.read()
data = json.loads(content)
table_headers = ["CVSS v3", "Severity", "File", "CVE/CWE"]
table_rows = []
vulns_distribution = {"DEP_WITHOUT_VULNS": 0, "DEP_WITH_VULNS": 0}
already_added = []
cve_list = []
for dependency in data["dependencies"]:
    name = dependency["fileName"]
    if "vulnerabilities" in dependency:
        vulns = dependency["vulnerabilities"]
        vulns_distribution["DEP_WITH_VULNS"] = vulns_distribution["DEP_WITH_VULNS"] + 1
        for vuln in vulns:
            cve = vuln["name"]
            if cve not in cve_list:
                cve_list.append(cve)
            severity = vuln["severity"].upper()
            if "cvssv3" in vuln:
                cvssv3 = vuln["cvssv3"]["baseScore"]
            elif "cvssv2" in vuln:
                cvssv3 = vuln["cvssv2"]["score"]
            else:
                cvssv3 = 0
            if severity == "HIGH" or severity == "CRITICAL":
                severity_color = "red"
            elif severity == "MEDIUM":
                severity_color = "yellow"
            elif severity == "LOW":
                severity_color = "cyan"
            else:
                severity_color = "white"
            severity = colored(f"{severity}", severity_color, attrs=["bold"])
            id = f"{severity}{name}{cve}{cvssv3}"
            if id not in already_added:
                table_rows.append([float(cvssv3), severity, name, cve])
                already_added.append(id)
    else:
        vulns_distribution["DEP_WITHOUT_VULNS"] = vulns_distribution["DEP_WITHOUT_VULNS"] + 1
table_rows.sort(reverse=True)
print(colored("[+] Vulnerabilities:", "yellow"))
print(tabulate(table_rows, headers=table_headers, numalign="right", stralign="left"))
print(" ")
print(colored("[+] Vulnerabilities severity distribution:", "yellow"))
vulns_distribution_severity_rows = []
severities = {}
severities_total = 0
for table_row in table_rows:
    severity = table_row[1].upper()
    if severity not in severities:
        severities[severity] = 0
    severities[severity] = severities[severity] + 1
    severities_total += 1
for severity, severity_count in severities.items():
    severity_no_color = re.findall(severity_no_color_regex, severity)[0]
    vulns_distribution_severity_rows.append([severity_no_color, severity_count, round((severity_count * 100) / severities_total)])
print(tabulate(vulns_distribution_severity_rows, headers=["Severity", "Vulnerability count", "Percentage distribution of severity"], numalign="right", stralign="left"))
print(" ")
if generate_csv_file_for_vulns:
    print(colored("[+] Generate CSV file 'vulns.csv'...", "yellow"), end="", flush=True)
    content = csv_separator_char.join(table_headers) + "\n"
    for row in table_rows:
        severity_no_color = re.findall(severity_no_color_regex, row[1])[0]
        content += f"{row[0]}{csv_separator_char}{severity_no_color}{csv_separator_char}{row[2]}{csv_separator_char}{row[3]}\n"
    with open("vulns.csv", "w", encoding="utf-8") as f:
        f.write(content)
    print("OK")
print(colored("[+] Vulnerabilities distribution:", "yellow"))
table_headers = ["Dependencies without vulnerability", "Dependencies with vulnerability", "Total count of dependencies", "Percentage of vulnerable dependencies", "Total count of vulnerabilities"]
dep_without_vulns = vulns_distribution["DEP_WITHOUT_VULNS"]
dep_with_vulns = vulns_distribution["DEP_WITH_VULNS"]
dep_total = vulns_distribution["DEP_WITH_VULNS"] + vulns_distribution["DEP_WITHOUT_VULNS"]
percentage_of_vuln_dep = round((dep_with_vulns * 100) / dep_total)
table_rows.clear()
table_rows.append([dep_without_vulns, dep_with_vulns, dep_total, percentage_of_vuln_dep, len(already_added)])
print(tabulate(table_rows, headers=table_headers,  numalign="right", stralign="left"))
print(" ")
print(colored(f"[+] List of CVE ({len(cve_list)}):", "yellow"))
cve_list.sort(reverse=True)
print("\n".join(cve_list))
print(" ")
