DBotPredictIncidentsBatch

Apply a trained ML model on multiple incidents at once, to compare incidents how the incidents were labeled by analysts, to the predictions of the model. This script is aimed to help evaluate a trained model using past incidents.

python · Machine Learning

Source

import copy

import demistomock as demisto
import pandas as pd
from CommonServerPython import *

from CommonServerUserPython import *

ALL_LABELS = "*"


def get_phishing_map_labels(comma_values):
    if comma_values == ALL_LABELS:
        return comma_values
    values = [x.strip() for x in comma_values.split(",")]
    labels_dict = {}
    for v in values:
        v = v.strip()
        if ":" in v:
            splited = v.rsplit(":", maxsplit=1)
            labels_dict[splited[0].strip()] = splited[1].strip()
        else:
            labels_dict[v] = v
    return dict(labels_dict.items())


def build_query_in_respect_to_phishing_labels(args):
    mapping = args.get("phishingLabels", ALL_LABELS)
    query = args.get("query", None)
    if mapping == ALL_LABELS:
        return args
    mapping_dict = get_phishing_map_labels(mapping)
    tag_field = args["tagField"]
    tags_union = " ".join([f'"{label}"' for label in mapping_dict])
    mapping_query = f"{tag_field}:({tags_union})"
    if "query" not in args:
        args["query"] = mapping_query
    else:
        args["query"] = f"({query}) and ({mapping_query})"
    return args


def main():
    d_args = dict(demisto.args())

    get_incidents_by_query_args = copy.deepcopy(d_args)
    get_incidents_by_query_args["NonEmptyFields"] = d_args["tagField"]
    subject_field_name = d_args.get("emailsubject").strip()  # type: ignore
    body_field_name = d_args.get("emailbody").strip()  # type: ignore
    html_field_name = d_args.get("emailbodyhtml").strip()  # type: ignore
    tag_field_name = d_args.get("tagField").strip()  # type: ignore
    additional_populate_fields = d_args.get("populateFields")
    populate_fields = [subject_field_name, body_field_name, html_field_name, tag_field_name, "id"]
    get_incidents_by_query_args["populateFields"] = ",".join([x.strip() for x in populate_fields if x is not None])
    if additional_populate_fields is not None and additional_populate_fields.strip() != "":
        get_incidents_by_query_args["populateFields"] = ",".join(
            [get_incidents_by_query_args["populateFields"], additional_populate_fields.strip()]
        )
    non_empty_fields = [subject_field_name, tag_field_name]
    get_incidents_by_query_args["NonEmptyFields"] = ",".join([x.strip() for x in non_empty_fields if x is not None])
    get_incidents_by_query_args = build_query_in_respect_to_phishing_labels(get_incidents_by_query_args)
    res = demisto.executeCommand("GetIncidentsByQuery", get_incidents_by_query_args)
    if is_error(res):
        return_error(get_error(res))
    incidents = json.loads(res[-1]["Contents"])

    email_subject_list = [i.get(subject_field_name, "") for i in incidents]
    email_body_list = [i.get(body_field_name, "") for i in incidents]
    email_html_list = [i.get(html_field_name, "") for i in incidents]

    if not (email_body_list or email_html_list) and email_subject_list:
        if not incidents:
            return_error("No incidents match the query.\n" f"Query arguments: !GetIncidentsByQuery {get_incidents_by_query_args}")
        return_error(
            "Incidents are missing fields.\nFields:\n"
            f"emailsubject: {email_subject_list}\nemailbody: {email_body_list}\nemailbodyhtml: {email_html_list}"
        )

    model_name = d_args.get("modelName")

    args = {
        "emailSubject": email_subject_list,
        "emailBody": email_body_list,
        "emailBodyHTML": email_html_list,
        "modelName": model_name,
    }
    res = demisto.executeCommand("DBotPredictPhishingWords", args)
    if is_error(res):
        return_error(get_error(res))

    incidents_df = pd.DataFrame(incidents)
    predictions_df = pd.DataFrame(res[-1]["Contents"])
    df = pd.concat([incidents_df, predictions_df], axis=1)
    df = df.rename(columns={"Label": "Prediction"})
    file_name = "predictions.csv"
    file_columns = ["id", tag_field_name, "Prediction", "Probability", "Error"]
    if additional_populate_fields is not None and additional_populate_fields.strip() != "":
        file_columns += [x.strip() for x in additional_populate_fields.split(",") if x.strip() != ""]
    file_columns = [c for c in file_columns if c in df.columns]
    filtered_df = df[file_columns]
    csv_data = filtered_df.to_csv()
    entry = fileResult(file_name, csv_data)
    entry["Contents"] = filtered_df.to_json(orient="records")
    entry["HumanReadable"] = f"File contains predictions of {len(incidents)} incidents"
    return entry


if __name__ in ["__main__", "__builtin__", "builtins"]:
    demisto.results(main())

README

Apply a trained ML model on multiple incidents at once, to compare incidents how the incidents were labeled by analysts, to the predictions of the model. This script is aimed to help evaluate a trained model using past incidents.

Script Data


Name Description
Script Type python3
Tags phishing, ml
Cortex XSOAR Version 5.0.0

Dependencies


This script uses the following commands and scripts.

  • GetIncidentsByQuery
  • DBotPredictPhishingWords

Used In


This script is used in the following playbooks and scripts.

  • VerifyOOBV2Predictions-Test

Inputs


Argument Name Description
query Additional text by which to query incidents.
incidentTypes A comma-separated list of incident types by which to filter.
fromDate The start date by which to filter incidents. Date format will be the same as in the incidents query page (valid strings exaple: “3 days ago”, ““2019-01-01T00:00:00 +0200”)
toDate The end date by which to filter incidents. Date format will be the same as in the incidents query page (valid strings exaple: “3 days ago”, ““2019-01-01T00:00:00 +0200”)
limit The maximum number of incidents to fetch.
tagField The field name with the label. Supports a comma-separated list, the first non-empty value will be taken.
hashSeed If non-empty, hash every word with this seed.
phishingLabels A comma-separated list of email tags values and mapping. The script considers only the tags specified in this field. You can map a label to another value by using this format: LABEL:MAPPED_LABEL. For example, for 4 values in email tag: malicious, credentials harvesting, inner communitcation, external legit email, unclassified. While training, we want to ignore “unclassified” tag, and refer to “credentials harvesting” as “malicious” too. Also, we want to merge “inner communitcation” and “external legit email” to one tag called “non-malicious”. The input will be: malicious, credentials harvesting:malicious, inner communitcation:non-malicious, external legit email:non-malicious
modelName The model name to store in the system.
emailsubject Incident field name with the email subject.
emailbody Incident field name with the email body (text).
emailbodyhtml Incident field name with the email body (html).
populateFields A comma-separated list of fields in the object to poplulate.

Outputs


There are no outputs for this script.