GIBDRPIncidentUpdate

This script prevents duplication of existing incidents. This automation runs using the default Limited User role, unless you explicitly change the permissions. For more information, see the section about permissions here: https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/6.10/Cortex-XSOAR-Administrator-Guide/Automations

python · Group-IB Digital Risk Protection

Source

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


IGNORE_UPDATE_KEYS = {
    "id",
    "investigationId",
}

REMOVE_BEFORE_UPDATE_KEYS = {
    "CustomFields",
    "labels",
    "occurred",
    "sla",
}


def _escape_lucene_phrase(value: str) -> str:
    if not isinstance(value, str):
        value = str(value)
    value = value.replace("\x00", "")
    value = value.replace("\r", " ").replace("\n", " ")
    return value.replace("\\", "\\\\").replace('"', '\\"')


def _extract_gibdrp_id(current_incident: dict[str, Any]) -> str | None:
    custom_fields = current_incident.get("CustomFields") or {}
    gibdrpid = custom_fields.get("gibdrpid")
    if gibdrpid:
        demisto.debug(f"GIBDRPIncidentUpdate: extracted gibdrpid from CustomFields: {gibdrpid}")
        return gibdrpid

    top_level = current_incident.get("gibdrpid")
    if top_level:
        demisto.debug(f"GIBDRPIncidentUpdate: extracted gibdrpid from top-level: {top_level}")
        return top_level

    raw_json_str = current_incident.get("rawJSON")
    if isinstance(raw_json_str, str) and raw_json_str:
        try:
            raw_obj = json.loads(raw_json_str)
            if not isinstance(raw_obj, dict):
                demisto.debug(
                    "GIBDRPIncidentUpdate: rawJSON parsed but is not a dict; "
                    f"type={type(raw_obj).__name__}. Skipping rawJSON extraction."
                )
                raw_obj = {}
            raw_id = raw_obj.get("id")
            if raw_id:
                demisto.debug(f"GIBDRPIncidentUpdate: extracted gibdrpid from rawJSON.id: {raw_id}")
                return raw_id
            raw_nested_id = (raw_obj.get("violation") or {}).get("id")
            if raw_nested_id:
                demisto.debug(f"GIBDRPIncidentUpdate: extracted gibdrpid from rawJSON.violation.id: {raw_nested_id}")
                return raw_nested_id
        except Exception as e:
            demisto.debug(f"GIBDRPIncidentUpdate: failed to parse rawJSON: {e!s}")

    mirror_id = current_incident.get("dbotMirrorId")
    if mirror_id:
        demisto.debug(f"GIBDRPIncidentUpdate: extracted gibdrpid from dbotMirrorId: {mirror_id}")
        return mirror_id

    demisto.debug("GIBDRPIncidentUpdate: gibdrpid not found in CustomFields, top-level, rawJSON or dbotMirrorId")
    return None


def _build_update_payload(current_incident: dict[str, Any]) -> dict[str, Any]:
    demisto.debug(f"GIBDRPIncidentUpdate: building update payload, incoming keys: {list(current_incident.keys())}")
    prepared: dict[str, Any] = {}
    base = dict(current_incident)
    custom_fields = base.get("CustomFields") or {}
    removed_keys: list[str] = []
    for key in REMOVE_BEFORE_UPDATE_KEYS:
        if key in base:
            base.pop(key, None)
            removed_keys.append(key)
    if removed_keys:
        demisto.debug(f"GIBDRPIncidentUpdate: removed transient keys before update: {removed_keys}")
    prepared.update(base)
    prepared.update(custom_fields)
    for forbidden in IGNORE_UPDATE_KEYS:
        prepared.pop(forbidden, None)
    demisto.debug(f"GIBDRPIncidentUpdate: prepared payload keys: {list(prepared.keys())}")
    return prepared


def _search_existing_incident_by_gibdrpid(gibdrpid: str) -> dict[str, Any] | None:
    query = f'gibdrpid:"{_escape_lucene_phrase(gibdrpid)}"'
    demisto.debug(f"GIBDRPIncidentUpdate: searching for duplicates with query: {query}")
    search_incident = demisto.executeCommand("getIncidents", {"query": query})
    if not search_incident:
        demisto.debug("GIBDRPIncidentUpdate: getIncidents returned empty result")
        return None
    contents = (search_incident[0] or {}).get("Contents") or {}
    total = int(contents.get("total", 0) or 0)
    data = contents.get("data") or []
    demisto.debug(f"GIBDRPIncidentUpdate: search results - total: {total}, items: {len(data) if isinstance(data, list) else 0}")
    if total <= 0 or not isinstance(data, list) or len(data) == 0:
        demisto.debug("GIBDRPIncidentUpdate: no existing incident found")
        return None
    chosen = data[-1]
    demisto.debug(f"GIBDRPIncidentUpdate: chosen existing incident id: {chosen.get('id')}")
    return chosen


def prevent_duplication(current_incident: dict[str, Any]) -> bool:
    demisto.debug(
        "GIBDRPIncidentUpdate: received incident for preprocessing - "
        f"type={current_incident.get('type')}, id={current_incident.get('id')}, "
        f"hasCustomFields={bool(current_incident.get('CustomFields'))}"
    )
    gibdrpid = _extract_gibdrp_id(current_incident)
    if not gibdrpid:
        demisto.debug("GIBDRPIncidentUpdate: gibdrpid not found on incoming incident; allowing creation.")
        return True

    existing = _search_existing_incident_by_gibdrpid(gibdrpid)
    if not existing:
        demisto.debug("GIBDRPIncidentUpdate: no duplicates found, creating new incident.")
        return True

    incident_id = existing.get("id")
    incident_gibdrpid = existing.get("gibdrpid")

    update_payload = _build_update_payload(current_incident)
    demisto.debug(f"GIBDRPIncidentUpdate: applying {len(update_payload)} fields to incident {incident_id}")
    for key, value in update_payload.items():
        demisto.debug(
            f"GIBDRPIncidentUpdate: Update incident key: {key} value: {value} "
            f"DataUpdate incident id: {incident_id} incident_gibdrpid: {incident_gibdrpid}"
        )
        demisto.executeCommand("setIncident", {"id": incident_id, key: value})
    demisto.debug(f"GIBDRPIncidentUpdate: Updated incident id: {incident_id} incident_gibdrpid: {incident_gibdrpid}")
    return False


def main():
    try:
        demisto.debug("GIBDRPIncidentUpdate: main invoked")
        return_results(prevent_duplication(demisto.incident()))
    except Exception as e:
        demisto.debug(f"GIBDRPIncidentUpdate: exception occurred: {e!s}")
        return_error(f"Error: {str(e)}")


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

README

This script prevents duplication of existing incidents.

Permissions


This automation runs using the default Limited User role, unless you explicitly change the permissions.
For more information, see the section about permissions here: https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/6.10/Cortex-XSOAR-Administrator-Guide/Automations

Script Data


Name Description
Script Type python3
Tags preProcessing
Cortex XSOAR Version 6.0.0

Inputs


There are no inputs for this script.

Outputs


There are no outputs for this script.