QualysCreateIncidentFromReport

Create incidents from a Qualys report (XML), based on the Qualys asset ID and vulnerability ID (QID). Duplicate incidents are not created for the same asset ID and QID.

python · Qualys

Source

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401
import json


def get_asset_id_for_ip(ip):
    resp = demisto.executeCommand("qualys-host-list", {"ips": ip})
    if isError(resp[0]):
        demisto.results(resp)
        sys.exit(0)

    if isinstance(resp_dict := resp[0], dict) and isinstance(xml_string := resp_dict["Contents"], str):
        json_string: str = xml2json(xml_string)
        asset_id = demisto.get(json.loads(json_string), "HOST_LIST_OUTPUT.RESPONSE.HOST_LIST.HOST.ID")
    else:
        asset_id = demisto.get(resp[0], "Contents.HOST_LIST_OUTPUT.RESPONSE.HOST_LIST.HOST.ID")

    return asset_id


def main():
    incident_type = demisto.args().get("incidentType", "Vulnerability")
    max_file_size = int(demisto.args().get("maxFileSize", 1024**2))
    min_severity = int(demisto.args().get("minSeverity", 1))

    file_entry = demisto.getFilePath(demisto.args().get("entryID"))
    with open(file_entry["path"]) as f:
        data = f.read(max_file_size)

    if data:
        report = json.loads(xml2json(data))

        generation_date = demisto.get(report, "ASSET_DATA_REPORT.HEADER.GENERATION_DATETIME")

        # Get asset list
        asset_list = demisto.get(report, "ASSET_DATA_REPORT.HOST_LIST.HOST")
        if not asset_list:
            demisto.results(
                {"Type": entryTypes["note"], "ContentsFormat": formats["text"], "Contents": "No vulnerable assets were found"}
            )
            sys.exit(0)

        if not isinstance(asset_list, list):
            asset_list = [asset_list]

        # Get QIDs only if over relevant severity
        general_vulnerabilities = argToList(demisto.get(report, "ASSET_DATA_REPORT.GLOSSARY.VULN_DETAILS_LIST.VULN_DETAILS"))
        if not isinstance(general_vulnerabilities, list):
            general_vulnerabilities = [general_vulnerabilities]

        # Get list of QID with severity >= min_severity
        qid_severity = [
            demisto.get(vulnerability, "QID.#text")
            for vulnerability in general_vulnerabilities
            if demisto.get(vulnerability, "SEVERITY") and (int(demisto.get(vulnerability, "SEVERITY")) >= min_severity)
        ]

        for asset in asset_list:
            # Get Asset ID from Qualys
            ip = demisto.get(asset, "IP")
            if not ip:
                demisto.results(
                    {
                        "Type": entryTypes["error"],
                        "ContentsFormat": formats["text"],
                        "Contents": f"No IP was found for asset {str(asset)}",
                    }
                )
                sys.exit(0)

            asset_id = get_asset_id_for_ip(ip)
            if not asset_id:
                demisto.results(
                    {
                        "Type": entryTypes["error"],
                        "ContentsFormat": formats["text"],
                        "Contents": f"No ID was found for asset {str(asset)}",
                    }
                )
                sys.exit(0)

            # Get Asset vulnerability list
            vulnerabilities = argToList(demisto.get(asset, "VULN_INFO_LIST.VULN_INFO"))
            if not isinstance(vulnerabilities, list):
                vulnerabilities = [vulnerabilities]

            qids = (demisto.get(vulnerability, "QID.#text") for vulnerability in vulnerabilities)

            # Get only the QIDs that exists in asset and has severity >= min_severity
            qids = list(set(qids) & set(qid_severity))

            for qid in qids:
                # Search for existing open incidents with the same Vendor ID and Asset ID.
                # Will open a new incident only if such an incident not exists.
                resp = demisto.executeCommand(
                    "getIncidents", {"query": f"vendorid: {qid} and assetid: {asset_id} and --status:Closed"}
                )
                if isError(resp[0]):
                    demisto.results(resp)
                    sys.exit(0)

                incident_number = demisto.get(resp[0], "Contents.total")

                try:
                    incident_number = int(incident_number)
                except Exception:
                    demisto.results(
                        {
                            "Type": entryTypes["error"],
                            "ContentsFormat": formats["text"],
                            "Contents": "Error while searching the incident repository",
                        }
                    )
                    sys.exit(0)

                if incident_number == 0:
                    # Create incident
                    demisto.executeCommand(
                        "createNewIncident",
                        {
                            "name": f"Vulnerability - Asset {asset_id} QID {qid} - {generation_date}",
                            "vendorid": str(qid),
                            "type": incident_type,
                            "assetid": str(asset_id),
                        },
                    )

        demisto.results("Done.")
    else:
        demisto.results({"Type": entryTypes["error"], "ContentsFormat": formats["text"], "Contents": "No data could be read."})


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()

README

Creates incidents from a Qualys report (XML), based on the Qualys asset ID and vulnerability ID (QID).
Duplicates the incidents that are not created for the same asset ID and QID.

Script Data


Name Description
Script Type python
Tags qualys

Dependencies


This script uses the following commands and scripts.

  • qualys-host-list

Inputs


Argument Name Description
entryID The War Room entryID of the XML report.
maxFileSize The maximum file size to load, in bytes. The default is 1024 KB.
minSeverity The minimum Qualys severity to create incidents for.
incidentType The incident type to create incidents for. The default is “Vulnerability”.

Outputs


There are no outputs for this script.