SecuronixCloseHistoricalXSOARIncidents

Close historical XSOAR incidents that are already closed on Securonix. NOTE: This script will close all the XSOAR incidents which are created from Securonix integration and does not have incident type as "Securonix Incident" in the provided time frame.

python · Securonix

Source

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

"""Script which closes the existing XSOAR incident whose respective Securonix incident is closed."""

import json
import traceback
from datetime import datetime
from typing import Any

close_xsoar_incident_ids = []


def get_securonix_incident_id(incident: dict[str, Any]) -> str | None:
    """Return Securonix incident id.

    Args:
        incident: Incident data.

    Returns:
        Optional[str]: Securonix incident ID if found.
    """
    incident_labels = incident.get("labels", [])

    for label in incident_labels:
        if label["type"] == "incidentId":
            return label["value"]
    return None


def is_incident_closed_on_securonix(activity_data: list[dict[str, Any]], close_states_of_securonix: list[str]) -> bool:
    """Check whether the incident is closed on the Securonix.

    Args:
        activity_data: A list of activity data from which to determine whether the incident is closed or not.
        close_states_of_securonix: A list of Securonix states which defines the close state for XSOAR.

    Returns:
        bool: Indicating whether the incident is closed on Securonix or not.
    """
    if not isinstance(activity_data, list):
        return False

    for activity in reversed(activity_data):
        current_status = activity.get("status", "").strip().lower()
        last_status = activity.get("lastStatus", "").strip().lower()

        if current_status in close_states_of_securonix and last_status not in close_states_of_securonix:
            return True

    return False


def extract_closing_comments(activity_data: list[dict[str, Any]], close_states_of_securonix: list[str]) -> str:
    """Extract the contents of the closing comments from activity data provided from Securonix.

    Args:
        activity_data: A list of activity data from which to extract the closing comments.
        close_states_of_securonix: A list of Securonix states which defines the close state for XSOAR.

    Returns:
        str: A string representing closing comments.
    """
    closing_comments = []

    if not isinstance(activity_data, list):
        return ""

    for activity in activity_data:
        current_status = activity.get("status", "").strip().lower()
        last_status = activity.get("lastStatus", "").strip().lower()

        if current_status in close_states_of_securonix and last_status not in close_states_of_securonix:
            comments_list = activity.get("comment", [])

            for _comment in comments_list:
                closing_comments.append(_comment.get("Comments", ""))

    if not closing_comments:
        closing_comments.append("Closing the XSOAR incident as Securonix incident is closed.")

    return " | ".join(closing_comments)


def close_xsoar_incident(xsoar_incident_id: str, sx_incident_id: str, close_states_of_securonix: list[str]) -> bool:
    """Close the existing XSOAR incident whose respective Securonix incident is closed.

    Args:
        xsoar_incident_id: XSOAR incident ID.
        sx_incident_id: Securonix incident ID.
        close_states_of_securonix: Close state of Securonix which can be considered as closed state of XSOAR.

    Returns:
        bool: True if the XSOAR incident is close, False otherwise.
    """
    demisto.debug(
        f"Getting update for XSOAR Incident: {xsoar_incident_id} from the respective Securonix Incident: {sx_incident_id}"
    )

    incident_activity_history_args = {"incident_id": sx_incident_id}
    incident_activity_history_resp = demisto.executeCommand(
        "securonix-incident-activity-history-get", args=incident_activity_history_args
    )

    try:
        incident_activity_history = incident_activity_history_resp[0]["Contents"]
    except KeyError as exception:
        demisto.error(str(exception))
        return False

    if is_incident_closed_on_securonix(incident_activity_history, close_states_of_securonix):
        demisto.info(f"Closing the XSOAR incident {xsoar_incident_id} as its respective securonix incident is closed.")
        closing_comments = extract_closing_comments(incident_activity_history, close_states_of_securonix)

        close_investigation_args = {
            "id": xsoar_incident_id,
            "closeNotes": closing_comments or "Incident closed using script SecuronixCloseHistoricalXSOARIncidents.",
            "closeReason": "Resolved",
        }
        demisto.executeCommand("closeInvestigation", close_investigation_args)
        return True

    demisto.info(f"The XSOAR Incident: {xsoar_incident_id} is not closed.Respective Securonix Incident: {sx_incident_id}.")
    return False


def main():
    """Entrypoint."""
    try:
        script_args = demisto.args()
        from_time = script_args.get("from", "").strip()
        to_time = script_args.get("to", "").strip()

        close_states_of_securonix = argToList(script_args.get("close_states", "").strip())
        close_states_of_securonix = [s.lower() for s in close_states_of_securonix]

        timestamp_format = "%Y-%m-%d %H:%M:%S.%f"

        if from_time:
            from_time = datetime.strftime(arg_to_datetime(from_time), timestamp_format)  # type: ignore[arg-type]
        if to_time:
            to_time = datetime.strftime(arg_to_datetime(to_time), timestamp_format)  # type: ignore[arg-type]

        xsoar_query = 'sourceBrand:Securonix and -type:"Securonix Incident" and -status:closed'

        page_num = 0
        number_of_incidents_closed = 0

        get_incidents_args = {"query": xsoar_query, "fromdate": from_time, "todate": to_time, "size": 100, "page": page_num}
        remove_nulls_from_dictionary(get_incidents_args)
        demisto.debug(f"getIncidents command arguments: {json.dumps(get_incidents_args)}")

        get_incidents_resp = demisto.executeCommand("getIncidents", get_incidents_args)
        xsoar_incidents = get_incidents_resp[0]["Contents"]["data"] or []
        total_xsoar_incidents = get_incidents_resp[0]["Contents"]["total"]

        demisto.info(f"Total number of incidents matched by the XSOAR query: {total_xsoar_incidents}")

        while True:
            if not xsoar_incidents:
                demisto.info("Completing the execution as no more incidents found!")
                break

            demisto.info(f"Starting to close {len(xsoar_incidents)} number of incidents.")

            for incident in xsoar_incidents:
                xsoar_incident_id = incident.get("id")
                sx_incident_id = get_securonix_incident_id(incident=incident)
                is_closed = close_xsoar_incident(xsoar_incident_id, sx_incident_id, close_states_of_securonix)  # type: ignore

                if is_closed:
                    close_xsoar_incident_ids.append(xsoar_incident_id)
                    number_of_incidents_closed += 1

            get_incidents_args["page"] = get_incidents_args["page"] + 1
            demisto.debug(f"getIncidents command arguments: {json.dumps(get_incidents_args)}")

            get_incidents_resp = demisto.executeCommand("getIncidents", get_incidents_args)
            xsoar_incidents = get_incidents_resp[0]["Contents"]["data"] or []

        return_results(
            CommandResults(
                readable_output=f"Successfully closed {number_of_incidents_closed} XSOAR incidents!",
                outputs_prefix="Securonix.CloseHistoricalXSOARIncidents",
                outputs_key_field="IncidentIDs",
                outputs=remove_empty_elements({"IncidentIDs": close_xsoar_incident_ids}),
            )
        )

    except Exception as exception:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Failed to execute SecuronixCloseHistoricalXSOARIncidents. Error: {exception!s}")


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

README

Close historical XSOAR incidents that are already closed on Securonix.

NOTE: This script will close all the XSOAR incidents which are created from Securonix integration and does not have incident type as “Securonix Incident” in the provided time frame.

Script Data


Name Description
Script Type python3
Cortex XSOAR Version 6.5.0

Dependencies


This script uses the following commands and scripts.

  • securonix-incident-activity-history-get

Inputs


Argument Name Description
from Filter the incidents which are created after the specified UTC date/time in XSOAR. (Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ, yyyy-MM-ddTHH:mm:ss.SSSZ. For example: 01 Jan 2023, 01 Feb 2023 04:45:33, 2023-01-26T14:05:44Z, 2023-01-26T14:05:44.000Z)
to Filter the incidents which are created before the specified UTC date/time in XSOAR. (Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ, yyyy-MM-ddTHH:mm:ss.SSSZ. For example: 01 Jan 2023, 01 Feb 2023 04:45:33, 2023-01-26T14:05:44Z, 2023-01-26T14:05:44.000Z)
close_states If the Securonix incident is in any one of the state mentioned here, then the incident will be Closed on XSOAR. Supports comma-separated values.

Outputs


Path Description Type
Securonix.CloseHistoricalXSOARIncidents.IncidentIDs List of XSOAR incident IDs that were closed. Unknown

Troubleshooting


The default timeout of this script is 1 hour. If you expect more number of incidents to be closed, then increase the
timeout of the script by using the execution-timeout argument. This argument expects the value to be passed in seconds.