iot-security-get-raci

IoT RACI model script.

python · IoT by Palo Alto Networks

Details

IDiot-security-get-raci
Languagepython
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Tagsiot

README

IoT RACI model script

Script Data


Name Description
Script Type python3
Tags iot
Cortex XSOAR Version 5.5.0

This script is using the device and incident attributes to evaluate the Responsible (R) and Informed (I) parties in the RACI model.

A list variable needs to be created with a fixed format JSON. You can create a new XSOAR list variable under Settings > Advanced > Lists.

By default, the name of the list variable is IOT_CONFIG.

There are three main sections in the JSON: devices, alerts, and groups.

“devices” is a list of devices mapping to the owners based on the device_id, which is a concatenation of the device’s category, profile, vendor and model delimited by “ ”.
  • device_id: a regular expression to match
  • owner: a group name, which is also defined in the “groups” section

“alerts” is a list of conditions to map a combination of IoT incident type and incident names to the RACI model.

  • iot_raw_type: either “IoT Alert” or “IoT Vulnerability”
  • name_regex: a list of regular expressions trying to match with the alert/vulnerability names
  • raci: a section to define the RACI model for the match. If the value is “IOT_OWNER”, we look up the underlying group using the mapping in “devices” section.

“groups” is all the groups found in the “devices” and “alerts” section.

  • email: the email of the group, this is used when setting the incident owner in XSOAR or sending an email through the email integration
  • snow: it has three fields, table, fields and custom_fields. Those are the fields when you use the official ServiceNow integration when you create a ServiceNow ticket.

Here is the template of the JSON:

{
    "devices": [
        {
            "device_id": "Audio Streaming|Profusion.*",
            "owner": "IT_AUDIO_VIDEO"
        },
        {
            "device_id": "Camera|Avigilon Camera.*",
            "owner": "PHYSICAL_SECURITY"
        }
    ],
    "alerts": [
        {
            "iot_raw_type": "IoT Alert",
            "name_regex": [
                "DOUBLEPULSAR.+",
                "ECLIPSEDWING.+",
                "ETERNALBLUE.+"
            ],
            "raci": {
                "r": "SOC",
                "i": ["IOT_OWNER"]
            }
        },
        {
            "iot_raw_type": "IoT Vulnerability",
            "raci": {
                "r": "IOT_OWNER",
                "i": ["INFOSEC", "SOC"]
            }
        }
    ],
    "groups": {
        "DEFAULT": {
            "email": "default@example.com"
        },
        "SOC": {
            "email": "soc@example.com"
        },
        "INFOSEC": {
            "email": "infosec@example.com"
        },
        "IT_AUDIO_VIDEO": {
            "email": "av@example.com",
            "snow": {
                "table": "incident",
                "fields": {
                    "assignment_group": "98dae8874fd67348bf547fe24210c7a0"
                },
                "custom_fields": {
                    "u_custom_field1": "IT",
                    "u_category": "05b9e5371b3b08905f28fc43cd4bcbe2"
                }
            }
        },
        "PHYSICAL_SECURITY": {
            "email": "security@example.com"
        }
    }
}

Used In


This script is used in the following playbooks and scripts.

  • PANW IoT Incident Handling with ServiceNow

Inputs


Argument Name Description
alert_name The name of the IoT alert.
raw_type The raw type of the incident.
category The device category.
profile The device profile.
vendor The device vendor.
model The device model.
iot_config_list_name The variable name for IOT_CONFIG.

Outputs


Path Description Type
PaloAltoNetworksIoT.RACI The RACI model of the IoT incident unknown
PaloAltoNetworksIoT.RACI.r The responsible in the RACI model string
PaloAltoNetworksIoT.RACI.r_email The email of responsible in the RACI model string
PaloAltoNetworksIoT.RACI.i The informed in the RACI model string
PaloAltoNetworksIoT.RACI.i_email The emails of informed in the RACI model delimited by comma string
PaloAltoNetworksIoT.RACI.owner The IoT owner of the device string
PaloAltoNetworksIoT.RACI.r_snow The ServiceNow information of the incident responsible string
PaloAltoNetworksIoT.RACI.r_snow.fields The fields of the ServiceNow ticket string
PaloAltoNetworksIoT.RACI.r_snow.custom_fields The custom fields of the ServiceNow ticket string
PaloAltoNetworksIoT.RACI.r_snow.table The table of the ServiceNow ticket string
import json
import re

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

from CommonServerUserPython import *  # noqa: E402 lgtm [py/polluting-import]


def get_iot_config(iot_config_list_name="IOT_CONFIG"):
    iot_config = demisto.executeCommand("getList", {"listName": iot_config_list_name})
    if is_error(iot_config):
        return None
    iot_config_content = iot_config[0]["Contents"]
    try:
        return json.loads(iot_config_content)
    except Exception as e:
        return_error(f"Failed to parse the IOT_CONFIG. Error: {e!s}")


def get_raci(args):
    alert_name = args.get("alert_name", "")
    alert_type = args.get("raw_type")

    category = args.get("category", "")
    profile = args.get("profile", "")
    vendor = args.get("vendor", "")
    model = args.get("model", "")
    device_id = f"{category}|{profile}|{vendor}|{model}"

    iot_config_list_name = args.get("iot_config_list_name", "IOT_CONFIG")
    config = get_iot_config(iot_config_list_name)
    if config is None:
        return None

    result = {}

    # determine owner
    owner = None
    for d in config.get("devices", []):
        if "device_id" in d and "owner" in d and re.match(d["device_id"], device_id):
            owner = d["owner"]
    result["owner"] = owner

    # determine raci
    raci = None
    for a in config.get("alerts", []):
        if "iot_raw_type" in a and "raci" in a and alert_type == a["iot_raw_type"]:
            match_name = "name_regex" not in a
            if not match_name:
                for n in a["name_regex"]:
                    if re.match(n, alert_name):
                        match_name = True
                        break
            if match_name:
                raci = a["raci"]

    if raci:
        r = raci["r"]
        if r == "IOT_OWNER":
            result["r"] = owner

            if owner is None:
                result["r_email"] = None
                result["r_snow"] = None
            else:
                e = config.get("groups", {}).get(owner, {}).get("email", None)
                if e is None:
                    default_email = config.get("groups", {}).get("DEFAULT", {}).get("email", None)
                    if default_email is not None:
                        result["r_email"] = default_email
                    else:
                        result["r_email"] = None
                else:
                    result["r_email"] = e

            result["r_snow"] = config.get("groups", {}).get(owner, {}).get("snow", None)
        elif r is not None:
            result["r"] = r

            e = config.get("groups", {}).get(r, {}).get("email", None)
            if e is None:
                default_email = config.get("groups", {}).get("DEFAULT", {}).get("email", None)
                if default_email is not None:
                    result["r_email"] = default_email
                else:
                    result["r_email"] = None
            else:
                result["r_email"] = e

            result["r_snow"] = config.get("groups", {}).get(r, {}).get("snow", None)
        else:
            result["r_email"] = None
            result["r_snow"] = None

        r_snow = result.get("r_snow", {})
        if r_snow:
            fields = r_snow.get("fields", {})
            if fields:
                r_snow["fields"] = ";".join([f"{k}={v}" for k, v in fields.items()])
            cfields = r_snow.get("custom_fields", {})
            if cfields:
                r_snow["custom_fields"] = ";".join([f"{k}={v}" for k, v in cfields.items()])

        i = []
        for inform in raci["i"]:
            if inform == "IOT_OWNER":
                if owner is not None:
                    i.append(owner)
            else:
                i.append(inform)
        result["i"] = ", ".join(i) if i else None

        if i:
            i_email = []
            for entry in i:
                e = config.get("groups", {}).get(entry, {}).get("email", None)
                if e is None:
                    default_email = config.get("groups", {}).get("DEFAULT", {}).get("email", None)
                    if default_email is not None:
                        i_email.append(default_email)
                else:
                    i_email.append(e)
            if len(i_email) > 0:
                result["i_email"] = ", ".join(i_email)
            else:
                result["i_email"] = None
    else:
        result["r"] = None
        result["r_email"] = None
        result["r_snow"] = None
        result["i"] = None
        result["i_email"] = None

    return CommandResults(outputs_prefix="PaloAltoNetworksIoT.RACI", outputs_key_field="", outputs=result)


def main():
    try:
        return_results(get_raci(demisto.args()))
    except Exception as ex:
        return_error(f"Failed to execute iot-security-get-raci. Error: {ex!s}")


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