DrawRelatedIncidentsCanvas
Draw incidents and indicators on the canvas to map and visualize their connections.
- Type
- python
- Pack
- 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.