__author__ = "phylu"

from dojo.models import Finding
import json


class CrashtestSecurityJsonParser(object):
    """
    The objective of this class is to parse a json file generated by the crashtest security suite.

    @param file A proper json file generated by the crashtest security suite
    @param test The test to which the finding belongs
    """

    def __init__(self, file, test):
        self.items = []

        # Load the data
        tree = file.read()
        try:
            crashtest_scan = json.loads(str(tree, 'utf-8'))
        except:
            crashtest_scan = json.loads(tree)

        # Extract the data from the data attribute if nested from original request
        if "data" in crashtest_scan:
            crashtest_scan = crashtest_scan["data"]

        self.descriptions = self.create_descriptions_dict(crashtest_scan["descriptions"])

        # Iterate scanner which contain the items
        for scanner in crashtest_scan["findings"].values():
            # Iterate all findings of the scanner
            for finding in scanner:
                self.items.append(self.generate_finding(finding, test))

                # Iterate all connected CVE findings if any
                if "cve_findings" in finding:
                    for cve_finding in finding["cve_findings"]:
                        self.items.append(self.generate_cve_finding(cve_finding, test))

    def create_descriptions_dict(self, data):
        """
        Create a dict containing the finding descriptions

        @param data The list of finding descriptions

        @return descritpnios A dict of descriptions with their slug as key
        """
        # Create descriptions dictionary
        descriptions = {}
        for description in data:
            descriptions[description["slug"]] = description
        return descriptions

    def generate_finding(self, finding, test):
        """
        Create a defect dojo Finding based on a crashtest security finding

        @param finding The crashtest security finding as dict from the json file
        @param test The test to which the finding belongs

        @return finding A finding as defect dojo Finding
        """
        description = self.descriptions[finding["finding_description_slug"]]
        severity = self.get_severity(description["baseScore"])
        impact = "CVSS Impact Score: {}".format(description["impact"])
        return Finding(
            title=description["title"],
            description=finding["information"],
            test=test,
            cve=None,
            severity=severity,
            mitigation=description["how_to_fix"],
            references=description["reference_resolution"],
            active=True,
            verified=False,
            false_p=False,
            duplicate=False,
            out_of_scope=False,
            mitigated=None,
            impact=impact,
            numerical_severity=Finding.get_numerical_severity(severity),
        )

    def generate_cve_finding(self, cve_finding, test):
        """
        Create a defect dojo Finding based on a crashtest security CVE finding

        @param finding The crashtest security CVE finding as dict from the json file
        @param test The test to which the finding belongs

        @return finding A finding as defect dojo Finding
        """
        severity = self.get_severity(cve_finding["cvss"])
        references = "https://nvd.nist.gov/vuln/detail/{}".format(cve_finding["cve_id"])
        return Finding(
            title=cve_finding["cve_id"],
            description=cve_finding["information"],
            test=test,
            cve=cve_finding["cve_id"],
            severity=severity,
            mitigation="No mitigation provided.",
            references=references,
            active=True,
            verified=False,
            false_p=False,
            duplicate=False,
            out_of_scope=False,
            mitigated=None,
            impact="No impact provided.",
            numerical_severity=Finding.get_numerical_severity(severity),
        )

    def get_severity(self, cvss_base_score):
        """
        Convert a cvss base score to a defect dojo severity level

        @param cvss_base_score Score between 0 and 10

        @return severity A severity string (Info, Low, Medium, High or Critical)
        """
        if cvss_base_score == 0:
            return "Info"
        elif cvss_base_score < 4:
            return "Low"
        elif cvss_base_score < 7:
            return "Medium"
        elif cvss_base_score < 9:
            return "High"
        else:
            return "Critical"
