DrawRelatedIncidentsCanvas
Draw incidents and indicators on the canvas to map and visualize their connections.
python · Base
Source
import math import demistomock as demisto import networkx as nx from CommonServerPython import * from CommonServerUserPython import * MINIMUM_SERVER_VERSION = "6.1.0" LAYOUT = demisto.args().get("layout") layout_to_functions = { "shell": lambda G: nx.shell_layout(G, scale=200), "spring": lambda G: nx.spring_layout(G, scale=400), "kamada_kawai": lambda G: nx.kamada_kawai_layout(G, scale=400), "circular": lambda G: nx.circular_layout(G, scale=600), "multipartite": lambda G: nx.multipartite_layout(G, scale=500, subset_key="layer", align="horizontal"), } def convert_position(_id, _type, xy_arr): return {"id": _id, "type": _type, "position": {"x": xy_arr[0], "y": xy_arr[1]}} def generate_layout(current_incident_id, incident_ids, indicator_ids, connections): G = nx.Graph() graph_incidents = ["incident-" + x for x in incident_ids] graph_indicators = ["indicator-" + x for x in indicator_ids] G.add_nodes_from(graph_incidents, layer=0) # type: ignore n_indicators = len(indicator_ids) n_indicators_lines = int(n_indicators / (len(incident_ids) + 1)) + 1 indicators_per_line = math.ceil(n_indicators / n_indicators_lines) for line in range(n_indicators_lines): layer = ( line - int(n_indicators_lines / 2) if line < int(n_indicators_lines / 2) else line - int(n_indicators_lines / 2) + 1 ) G.add_nodes_from(graph_indicators[indicators_per_line * line : indicators_per_line * (line + 1)], layer=layer) for connection in connections: src = f"{connection['srcEntityType']}-{connection['srcEntityId']}" dest = f"{connection['targetEntityType']}-{connection['targetEntityId']}" G.add_edge(src, dest) pos = layout_to_functions.get(LAYOUT)(G) # type: ignore layout = {} for k, v in pos.items(): _type = k.split("-")[0] layout[k] = convert_position(k, _type, v) if LAYOUT == "shell": layout["incident-" + current_incident_id]["position"]["x"] = 0 layout["incident-" + current_incident_id]["position"]["y"] = 0 return layout def get_incident_to_incident_connection(inc1, inc2): return {"srcEntityType": "incident", "srcEntityId": inc1, "targetEntityType": "incident", "targetEntityId": inc2} def get_incident_to_indicator_connection(inc, ioc): return {"srcEntityType": "incident", "srcEntityId": inc, "targetEntityType": "indicator", "targetEntityId": ioc} def generate_canvas(current_incident_id, incident_ids, indicators): indicator_ids = [x["id"] for x in indicators] incident_nodes = [{"type": "incident", "id": x} for x in incident_ids] indicators_nodes = [{"type": "indicator", "id": x} for x in indicator_ids] nodes = incident_nodes + indicators_nodes incident_to_other_incidents = [] for incident_id in incident_ids: if incident_id != current_incident_id: incident_to_other_incidents.append(get_incident_to_incident_connection(current_incident_id, incident_id)) incident_to_indicator_connections = [] for indicator in indicators: for investigation_id in indicator.get("investigationIDs", []): if investigation_id in incident_ids + [current_incident_id]: incident_to_indicator_connections.append(get_incident_to_indicator_connection(investigation_id, indicator["id"])) connections = incident_to_other_incidents + incident_to_indicator_connections canvas = { "incidentID": current_incident_id, "version": -1, "nodes": nodes, "layout": generate_layout(current_incident_id, incident_ids, indicator_ids, connections), } return canvas, connections def main(): if not is_demisto_version_ge(MINIMUM_SERVER_VERSION): return_error(f"This script is supported only from version {MINIMUM_SERVER_VERSION}.") current_incident_id = demisto.args().get("incidentID") if current_incident_id is None: incident = demisto.incident() current_incident_id = incident["id"] related_incidents_ids = demisto.args().get("relatedIncidentsIDs", []) if type(related_incidents_ids) is not list: related_incidents_ids = related_incidents_ids.split(",") related_incidents_ids = [x for x in related_incidents_ids if x] indicators = demisto.args().get("indicators", []) if len(indicators) and len(related_incidents_ids) == 0: return_error("No related incidents or indicators specified") canvas, connections = generate_canvas(current_incident_id, related_incidents_ids, indicators) override = demisto.args().get("overrideUserCanvas") == "true" res = demisto.executeCommand( "drawCanvas", {"canvas": canvas, "canvasLinks": connections, "id": current_incident_id, "overrideUserCanvas": override} ) if res is None: return_error("Unexpected error") elif is_error(res): return_error(get_error(res)) else: hr = f"### Check the incidents and indicators layout on the [canvas](#/Canvas/{current_incident_id})" return_outputs(hr) main()
README
Draw incidents and indicators on the canvas to map and visualize their connections.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Cortex XSOAR Version | 6.1.0 |
Inputs
| Argument Name | Description |
|---|---|
| incidentID | Incident ID of the incident with which to update the canvas. If not specified, updates the current incident. |
| relatedIncidentsIDs | Incident IDs to draw on the canvas and relate to the main incident. The format can be a list of IDs or comma-separated values. |
| indicators | Indicators to draw on the canvas. The format is a list of indicator objects. |
| layout | The canvas layout. Can be “multipartite”, “shell”, “spring”, “kamada_kawai”, or “circular”. |
| overrideUserCanvas | Override the canvas if it exists. |
Outputs
There are no outputs for this script.