SearchIncidentsV2
Searches Demisto incidents. A summarized version of this scrips is available with the summarizedversion argument. This automation runs using the default Limited User role, unless you explicitly change the permissions. For more information, see the section about permissions here: - For Cortex XSOAR 6 see https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/6.x/Cortex-XSOAR-Playbook-Design-Guide/Automations - For Cortex XSOAR 8 Cloud see https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/8/Cortex-XSOAR-Cloud-Documentation/Create-a-script - For Cortex XSOAR 8.7 On-prem see https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/8.7/Cortex-XSOAR-On-prem-Documentation/Create-a-script https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/6.10/Cortex-XSOAR-Administrator-Guide/Automations
python · Common Scripts
Source
from enum import Enum import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 DEFAULT_LIMIT = 100 DEFAULT_PAGE_SIZE = 100 STARTING_PAGE_NUMBER = 1 class AlertSeverity(Enum): UNKNOWN = 0 INFO = 0.5 LOW = 1 MEDIUM = 2 HIGH = 3 CRITICAL = 4 class AlertStatus(Enum): PENDING = 0 ACTIVE = 1 DONE = 2 ARCHIVE = 3 def check_if_found_incident(res: List): if res and isinstance(res, list) and isinstance(res[0].get("Contents"), dict): if "data" not in res[0]["Contents"]: raise DemistoException(res[0].get("Contents")) elif res[0]["Contents"]["data"] is None: return False return True else: raise DemistoException(f"failed to get incidents from xsoar.\nGot: {res}") def is_valid_args(args: Dict): array_args: List[str] = ["id", "name", "status", "notstatus", "reason", "level", "owner", "type", "query"] error_msg: List[str] = [] for _key, value in args.items(): if _key in array_args: try: if _key == "id": if not isinstance(value, int | str | list): error_msg.append( f"Error while parsing the incident id with the value: {value}. The given type: " f"{type(value)} is not a valid type for an ID. The supported id types are: int, list and str" ) elif isinstance(value, str): _ = bytes(value, "utf-8").decode("unicode_escape") elif _key == "query": _ = bytes(value.replace("\\", "\\\\"), "utf-8").decode("unicode_escape") else: _ = bytes(value, "utf-8").decode("unicode_escape") except UnicodeDecodeError as ex: error_msg.append(f'Error while parsing the argument: "{_key}" \nError:\n- "{ex!s}"') if len(error_msg) != 0: raise DemistoException("\n".join(error_msg)) return True def apply_filters(incidents: List, args: Dict): names_to_filter = set(argToList(args.get("name"))) types_to_filter = set(argToList(args.get("type"))) filtered_incidents = [] for incident in incidents: incident_id, incident_type = incident.get("id"), incident.get("type") if names_to_filter and incident["name"] not in names_to_filter: continue demisto.debug(f"{incident_id=}, {incident_type=}") if types_to_filter and incident["type"] not in types_to_filter: continue filtered_incidents.append(incident) return filtered_incidents def summarize_incidents(args: dict, incidents: List[dict], platform: str): summarized_fields = [ "id", "name", "type", "severity", "status", "owner", "created", "closed", ] if platform == "x2" or platform == "unified_platform": summarized_fields.append("alertLink") else: summarized_fields.append("incidentLink") if args.get("add_fields_to_summarize_context"): summarized_fields += args.get("add_fields_to_summarize_context", "").split(",") summarized_fields = [x.strip() for x in summarized_fields] # clear out whitespace summarized_incidents = [] for incident in incidents: summarized_incident = { field: incident.get(field, incident["CustomFields"].get(field, "n/a")) for field in summarized_fields } summarized_incidents.append(summarized_incident) return summarized_incidents def add_incidents_link(data: List, platform: str): # For XSIAM or Platform links if platform == "x2" or platform == "unified_platform": server_url = "https://" + demisto.getLicenseCustomField("Http_Connector.url") for incident in data: incident_link = urljoin(server_url, f'alerts?action:openAlertDetails={incident.get("id")}-investigation') incident["alertLink"] = incident_link # For XSOAR links else: server_url = demisto.demistoUrls().get("server") prefix = "" if is_demisto_version_ge("8.4.0") else "#" for incident in data: incident_link = urljoin(server_url, f'{prefix}/Details/{incident.get("id")}') incident["incidentLink"] = incident_link return data def transform_to_alert_data(incidents: List): for incident in incidents: incident["hostname"] = incident.get("CustomFields", {}).get("hostname") incident["initiatedby"] = incident.get("CustomFields", {}).get("initiatedby") incident["targetprocessname"] = incident.get("CustomFields", {}).get("targetprocessname") incident["username"] = incident.get("CustomFields", {}).get("username") incident["status"] = AlertStatus(incident.get("status")).name incident["severity"] = AlertSeverity(incident.get("severity")).name return incidents def search_incidents(args: Dict): # pragma: no cover hr_prefix = "" is_summarized_version = argToBoolean(args.get("summarizedversion", False)) platform = get_demisto_version().get("platform", "xsoar") if not is_valid_args(args): return None if fromdate := arg_to_datetime(args.get("fromdate", "30 days ago" if is_summarized_version else None)): from_date = fromdate.isoformat() args["fromdate"] = from_date if todate := arg_to_datetime(args.get("todate")): to_date = todate.isoformat() args["todate"] = to_date if args.get("trimevents"): if platform == "xsoar" or platform == "xsoar_hosted": raise ValueError("The trimevents argument is not supported in XSOAR.") if args.get("trimevents") == "0": args.pop("trimevents") if includeinformational := argToBoolean(args.get("includeinformational", False)): if not (args.get("fromdate") and args.get("todate")): raise ValueError("The includeinformational argument requires fromdate and todate arguments.") if (datetime.utcnow() - fromdate).total_seconds() > 5 * 60 * 60: # type: ignore args["fromdate"] = arg_to_datetime("5 hours ago").isoformat() # type: ignore hr_prefix = ( f"fromdate: {fromdate} is more than 5 hours ago. We support " f"querying informational incidents for up to the last 5 hours. The fromdate has been" f" adjusted to 5 hours ago." ) args["includeinformational"] = includeinformational else: args.pop("includeinformational", None) # handle list of ids if args.get("id"): args["id"] = ",".join(argToList(args.get("id"), transform=str)) res: List = execute_command("getIncidents", args, extract_contents=False) incident_found: bool = check_if_found_incident(res) if not incident_found: if hr_prefix: hr_prefix = f"{hr_prefix}\n" if platform == "x2" or platform == "unified_platform": return f"{hr_prefix}Alerts not found.", {}, {} return f"{hr_prefix}Incidents not found.", {}, {} limit = arg_to_number(args.get("limit")) or DEFAULT_LIMIT all_found_incidents = res[0]["Contents"]["data"] demisto.debug(f"Amount of incidents before filtering = {len(all_found_incidents)} with args {args} before pagination") page_size = args.get("size") or DEFAULT_PAGE_SIZE more_pages = len(all_found_incidents) == page_size all_found_incidents = add_incidents_link(apply_filters(all_found_incidents, args), platform) demisto.debug(f"Amount of incidents after filtering = {len(all_found_incidents)} before pagination") page = STARTING_PAGE_NUMBER if all_found_incidents and "todate" not in args: # In case todate is not part of the argumetns we add it to avoid duplications first_incident = all_found_incidents[0] args["todate"] = first_incident.get("created") demisto.info(f"Setting todate argument to be {first_incident.get('created')} to avoid duplications") while more_pages and len(all_found_incidents) < limit: args["page"] = page current_page_found_incidents = execute_command("getIncidents", args).get("data") or [] # When current_page_found_incidents is None it means the requested page was empty if not current_page_found_incidents: break demisto.debug(f"before filtering {len(current_page_found_incidents)=} {args=} {page=}") more_pages = len(current_page_found_incidents) == page_size current_page_found_incidents = add_incidents_link(apply_filters(current_page_found_incidents, args), platform) demisto.debug(f"after filtering = {len(current_page_found_incidents)=}") all_found_incidents.extend(current_page_found_incidents) page += 1 all_found_incidents = all_found_incidents[:limit] additional_headers: List[str] = [] if is_summarized_version: all_found_incidents = summarize_incidents(args, all_found_incidents, platform) if args.get("add_fields_to_summarize_context"): additional_headers = args.get("add_fields_to_summarize_context", "").split(",") headers: List[str] if platform == "x2" or platform == "unified_platform": headers = [ "id", "name", "severity", "details", "hostname", "initiatedby", "status", "owner", "targetprocessname", "username", "alertLink", ] all_found_incidents = transform_to_alert_data(all_found_incidents) md = tableToMarkdown( name="Alerts found", t=all_found_incidents, headers=headers + additional_headers, removeNull=True, url_keys=["alertLink"], ) else: headers = ["id", "name", "severity", "status", "owner", "created", "closed", "incidentLink"] md = tableToMarkdown( name="Incidents found", t=all_found_incidents, headers=headers + additional_headers, url_keys=["incidentLink"] ) if hr_prefix: md = f"{hr_prefix}\n{md}" demisto.debug(f"amount of all the incidents that were found {len(all_found_incidents)}") return md, all_found_incidents, res def main(): # pragma: no cover args: Dict = demisto.args() is_summarized_version = argToBoolean(args.get("summarizedversion", False)) try: readable_output, outputs, raw_response = search_incidents(args) if search_results_label := args.get("searchresultslabel"): for output in outputs: output["searchResultsLabel"] = search_results_label results = CommandResults( outputs_prefix="foundIncidents", outputs_key_field="id", readable_output=readable_output, outputs=outputs, raw_response=raw_response, # in summarized version, ignore auto extract ignore_auto_extract=is_summarized_version, ) return_results(results) except DemistoException as error: return_error(str(error), error) if __name__ in ("__main__", "__builtin__", "builtins"): # pragma: no cover main()
README
Searches Demisto incidents. A summarized version of this scrips is available with the summarizedversion argument.
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: <~XSOAR>For Cortex XSOAR 6, see the https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/6.x/Cortex-XSOAR-Playbook-Design-Guide/Automations for Cortex XSOAR 8 Cloud, see the https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/8/Cortex-XSOAR-Cloud-Documentation/Create-a-script for Cortex XSOAR 8 On-prem, see the https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/8.7/Cortex-XSOAR-On-prem-Documentation/Create-a-script.</~XSOAR><~XSIAM>https://docs-cortex.paloaltonetworks.com/r/Cortex-XSIAM/Cortex-XSIAM-Administrator-Guide/Permission-Management</~XSIAM>
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | Utility |
| Cortex XSOAR Version | 5.0.0 |
Used In
Sample usage of this script can be found in the following playbooks and scripts.
- Endpoint Investigation Plan
- ExtraHop - Ticket Tracking
- Kaseya VSA 0-day - REvil Ransomware Supply Chain Attack
- MDE - False Positive Incident Handling
- MDE - True Positive Incident Handling
- Prisma Cloud Correlate Alerts v2
- Ransomware Enrich and Contain
- SafeBreach - Create Incidents per Insight and Associate Indicators
- SolarStorm and SUNBURST Hunting and Response Playbook
- Spring Core and Cloud Function SpEL RCEs
Inputs
| Argument Name | Description |
|---|---|
| id | A comma-separated list of incident IDs by which to filter the results. |
| name | A comma-separated list of incident names by which to filter the results. |
| status | A comma-separated list of incident statuses by which to filter the results. For example: assigned. |
| notstatus | A comma-separated list of incident statuses to exclude from the results. For example: assigned. |
| reason | A comma-separated list of incident close reasons by which to filter the results. |
| fromdate | Filter by from date (e.g. “3 days ago” or 2006-01-02T15:04:05+07:00 or 2006-01-02T15:04:05Z). Default value is “30 days ago”. |
| todate | Filter by to date (e.g. “3 days ago” or 2006-01-02T15:04:05+07:00 or 2006-01-02T15:04:05Z) |
| fromclosedate | Filter by from close date (e.g. 2006-01-02T15:04:05+07:00 or 2006-01-02T15:04:05Z) |
| toclosedate | Filter by to close date (e.g. 2006-01-02T15:04:05+07:00 or 2006-01-02T15:04:05Z) |
| fromduedate | Filter by from due date (e.g. 2006-01-02T15:04:05+07:00 or 2006-01-02T15:04:05Z) |
| toduedate | Filter by to due date (e.g. 2006-01-02T15:04:05+07:00 or 2006-01-02T15:04:05Z) |
| level | Filter by Severity |
| owner | Filter by incident owners |
| details | Filter by incident details |
| type | Filter by incident type |
| query | Use free form query (use Lucene syntax) as filter. All other filters will be ignored when this filter is used. |
| page | Filter by the page number (deprecated) |
| trimevents | The number of events to return from the alert JSON. The default is 0, which returns all events. Note that the count is from the head of the list, regardless of event time or other properties. |
| size | Number of incidents per page (per fetch) (deprecated) |
| sort | Sort in format of field.asc,field.desc,… |
| searchresultslabel | If provided, the value of this argument will be set under the searchResultsLabel context key for each incident found. |
| summarizedversion | If enabled runs a summarized version of this script. Disables auto-extract, sets fromDate to 30 days, and minimizes the context output. You can add sepcific fields to context using the add_fields_to_summarize_context argument. Default is false. |
| includeinformational | Supported only in XSIAM. When the value is set to ‘True’, informational severity alerts will return as part of the results. The ‘fromdate’ and ‘todate’ arguments must be provided to use this argument. The maximum value currently supported for the ‘fromdate’ argument to retrieve informational incidents is 5 hours. If a value greater than this is provided, it will be adjusted to 5 hours ago. To retrieve only informational incidents, use the query argument and include this limitation within the query. Default is false. |
| limit | The maximum number of incidents to be returned. Default is 100. |
Outputs
| Path | Description | Type |
|---|---|---|
| foundIncidents.id | A list of incident IDs returned from the query. | Unknown |
| foundIncidents.name | A list of incident names returned from the query. | Unknown |
| foundIncidents.severity | A list of incident severities returned from the query. | Unknown |
| foundIncidents.status | A list of incident statuses returned from the query. | Unknown |
| foundIncidents.owner | A list of incident owners returned from the query. | Unknown |
| foundIncidents.created | A list of the incident create date returned from the query. | Unknown |
| foundIncidents.closed | A list of incident close dates returned from the query. | Unknown |
| foundIncidents.labels | An array of labels per incident returned from the query. | Unknown |
| foundIncidents.details | Details of the incidents returned from the query. | Unknown |
| foundIncidents.dueDate | A list of incident due dates returned from the query. | Unknown |
| foundIncidents.phase | A list of incident phases returned from the query. | Unknown |
| foundIncidents.searchResultsLabel | The value provided in the searchresultslabel argument. | String |
Script Example
!SearchIncidentsV2 name="Incident to search"
Context Example
{
"foundIncidents": [
{
"CustomFields": {
"detectionsla": {
"accumulatedPause": 0,
"breachTriggered": false,
"dueDate": "0001-01-01T00:00:00Z",
"endDate": "0001-01-01T00:00:00Z",
"lastPauseDate": "0001-01-01T00:00:00Z",
"runStatus": "idle",
"sla": 20,
"slaStatus": -1,
"startDate": "0001-01-01T00:00:00Z",
"totalDuration": 0
},
"remediationsla": {
"accumulatedPause": 0,
"breachTriggered": false,
"dueDate": "0001-01-01T00:00:00Z",
"endDate": "0001-01-01T00:00:00Z",
"lastPauseDate": "0001-01-01T00:00:00Z",
"runStatus": "idle",
"sla": 7200,
"slaStatus": -1,
"startDate": "0001-01-01T00:00:00Z",
"totalDuration": 0
},
"timetoassignment": {
"accumulatedPause": 0,
"breachTriggered": false,
"dueDate": "0001-01-01T00:00:00Z",
"endDate": "0001-01-01T00:00:00Z",
"lastPauseDate": "0001-01-01T00:00:00Z",
"runStatus": "idle",
"sla": 0,
"slaStatus": -1,
"startDate": "0001-01-01T00:00:00Z",
"totalDuration": 0
},
"urlsslverification": []
},
"ShardID": 0,
"account": "",
"activated": "0001-01-01T00:00:00Z",
"allRead": false,
"allReadWrite": false,
"attachment": null,
"autime": 1601389784162034000,
"canvases": null,
"category": "",
"closeNotes": "",
"closeReason": "",
"closed": "0001-01-01T00:00:00Z",
"closingUserId": "",
"created": "2020-09-29T17:29:44.162034+03:00",
"dbotCreatedBy": "admin",
"dbotCurrentDirtyFields": null,
"dbotDirtyFields": null,
"dbotMirrorDirection": "",
"dbotMirrorId": "",
"dbotMirrorInstance": "",
"dbotMirrorLastSync": "0001-01-01T00:00:00Z",
"dbotMirrorTags": null,
"details": "",
"droppedCount": 0,
"dueDate": "2020-10-09T17:29:44.162034+03:00",
"feedBased": false,
"hasRole": false,
"id": "978",
"investigationId": "",
"isPlayground": false,
"labels": [
{
"type": "Instance",
"value": "admin"
},
{
"type": "Brand",
"value": "Manual"
}
],
"lastJobRunTime": "0001-01-01T00:00:00Z",
"lastOpen": "0001-01-01T00:00:00Z",
"linkedCount": 0,
"linkedIncidents": null,
"modified": "2020-09-29T17:29:44.162202+03:00",
"name": "Incident to search",
"notifyTime": "0001-01-01T00:00:00Z",
"occurred": "2020-09-29T17:29:44.162034+03:00",
"openDuration": 0,
"owner": "admin",
"parent": "",
"phase": "",
"playbookId": "",
"previousAllRead": false,
"previousAllReadWrite": false,
"previousRoles": null,
"rawCategory": "",
"rawCloseReason": "",
"rawJSON": "",
"rawName": "Incident to search",
"rawPhase": "",
"rawType": "Unclassified",
"reason": "",
"reminder": "0001-01-01T00:00:00Z",
"roles": null,
"runStatus": "",
"severity": 0,
"sla": 0,
"sortValues": [
"_score"
],
"sourceBrand": "Manual",
"sourceInstance": "admin",
"status": 0,
"type": "Unclassified",
"version": 1
}
]
}
Human Readable Output
Incidents found
| id | name | severity | status | owner | created | closed |
|---|---|---|---|---|---|---|
| 978 | Incident to search | 0 | 0 | admin | 2020-09-29T17:29:44.162034+03:00 | 0001-01-01T00:00:00Z |