HashIncidentsFields

Hash fields from the incident list. Search for incidents by arguments with an option to hash some of its fields.

python · Machine Learning

Source

import base64
import hashlib
import pickle
import uuid

from CommonServerPython import *

RANDOM_UUID = str(demisto.args().get("addRandomSalt", ""))
# Memo for key matching
CACHE = {}  # type: ignore


def hash_value(simple_value):
    if not isinstance(simple_value, str):
        simple_value = str(simple_value)
    if simple_value.lower() in ["none", "null"]:
        return None
    if RANDOM_UUID:
        simple_value += RANDOM_UUID
    return hashlib.md5(simple_value.encode("utf8")).hexdigest()  # nosec


def pattern_match(pattern, s):
    regex = re.compile(pattern.replace("*", ".*"))
    return re.match(regex, s) is not None


def is_key_match_fields_to_hash(key, fields_to_hash):
    if key is None:
        return False

    if key in CACHE:
        return CACHE[key]
    for field in fields_to_hash:
        if pattern_match(field, key):
            CACHE[key] = True
            return True
    CACHE[key] = False
    return False


def hash_incident_labels(incident_labels, fields_to_hash):
    labels = []
    if isinstance(incident_labels, list):
        for label in incident_labels:
            if is_key_match_fields_to_hash(label.get("type"), fields_to_hash):
                value = label.get("value") or ""
                if value:
                    label["value"] = hash_value(value)
            labels.append(label)
    return labels


def hash_multiple(value, fields_to_hash, to_hash=False):
    if isinstance(value, list):
        return [hash_multiple(x, fields_to_hash, to_hash) for x in value]
    if isinstance(value, dict):
        for k, v in value.items():
            _hash = to_hash or is_key_match_fields_to_hash(k, fields_to_hash)
            value[k] = hash_multiple(v, fields_to_hash, _hash)
        return value
    else:
        try:
            if isinstance(value, int | float | bool):
                to_hash = False
            if not isinstance(value, str):
                value = str(value)
        except Exception:
            value = ""
        if to_hash and value:
            return hash_value(value)
        else:
            return value


def output_file(data, description, output_format):
    data_encoded = b""
    file_name = str(uuid.uuid4())
    if output_format == "pickle":
        pickled_incidents = []
        for i in data:
            pickled_incident = base64.b64encode(pickle.dumps(i))
            pickled_incidents.append(pickled_incident)
        data_encoded = pickle.dumps(pickled_incidents)
    elif output_format == "json":
        data_encoded = json.dumps(data).encode("utf8")
    else:
        return_error(f"Invalid output format: {output_format}")
    entry = fileResult(file_name, data_encoded)
    entry["Contents"] = data
    entry["HumanReadable"] = description
    entry["EntryContext"] = {
        "HashIncidentsFields": {
            "Filename": file_name,
            "FileFormat": output_format,
        }
    }
    return entry


def copy_key_from_context(ctx, context_keys):
    new_ctx = {}
    if not ctx:
        return {}
    for key in context_keys:
        new_ctx[key] = demisto.dt(ctx, key)
    return new_ctx


def get_context(incident_id):
    res = demisto.executeCommand("getContext", {"id": incident_id})
    try:
        return res[0]["Contents"].get("context") or {}
    except Exception:
        return {}


def hash_incident(fields_to_hash, un_populate_fields):
    args = demisto.args()

    remove_labels = demisto.args().get("removeLabels", "") == "true"
    context_keys = [x for x in argToList(demisto.args().get("contextKeys", "")) if x]

    # load incidents
    res = demisto.executeCommand("GetIncidentsByQuery", args)
    if is_error(res):
        return_error(get_error(res))
    incident_list = json.loads(res[0]["Contents"])

    # filter incidents
    new_incident_list = []
    for incident in incident_list:
        if context_keys:
            incident["context"] = copy_key_from_context(get_context(incident["id"]), context_keys)

        # remove CustomFields
        incident.pop("CustomFields", None)

        incident = hash_multiple(incident, fields_to_hash)

        # filter out fields
        if un_populate_fields:
            incident = {k: v for k, v in incident.items() if k not in un_populate_fields}

        # remove or hash incident labels
        incident_labels = incident.pop("labels")
        if not remove_labels:
            incident["labels"] = hash_incident_labels(incident_labels, fields_to_hash)

        new_incident_list.append(incident)

    # Output
    desc = f"Fetched {len(new_incident_list)} incidents successfully by the query: {args.get('query')}"
    entry = output_file(new_incident_list, desc, args["outputFormat"])
    return entry


if __name__ in ["__main__", "__builtin__", "builtins"]:
    args = demisto.args()
    fields_to_hash = frozenset([x for x in argToList(args.get("fieldsToHash", "")) if x])
    un_populate_fields = frozenset([x for x in argToList(args.get("unPopulateFields", "")) if x])
    entry = hash_incident(fields_to_hash, un_populate_fields)
    demisto.results(entry)

README

Hash fields from the incident list.
Search for incidents by arguments with an option to hash some of its fields.

Script Data


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

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, for example: “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, for example: “3 days ago”, ““2019-01-01T00:00:00 +0200”).
limit The maximum number of incidents to fetch. The default value is 3000.
timeField The incident field to specify for the date range. Can be “created” or “modified”. The default is “created”.
NonEmptyFields A comma-separated list of non-empty value incident field names by which to filter incidents.
outputFormat The output file format.
populateFields A comma-separated list of fields in the object to poplulate.
fieldsToHash A comma-separated list of fields to hash. Support wildcards.
contextKeys A comma-separated list of context keys to keep.
removeLabels Remove incident labels
unPopulateFields A comma-separated list of fields in the object to un-poplulate.
addRandomSalt Random salt to the hash function

Outputs


Path Description Type
HashIncidentsFields.Filename The output file name. String
HashIncidentsFields.FileFormat The output file format. String