Stealthwatch Cloud
Protect your cloud assets and private network.
Network Security · Cisco Secure Cloud Analytics (Stealthwatch Cloud)
Details
| ID | Stealthwatch Cloud |
|---|---|
| Provider | Cisco Systems |
| Category | Network Security |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10325753 |
| Supported Modules | Agentix XSIAM EDR Cortex Cloud Cloud Runtime Security |
README
Overview
Use the Cisco Secure Cloud Analytics (Stealthwatch Cloud) integration to manage threats to your networks.
This integration was integrated and tested with Cisco Secure Cloud Analytics (Stealthwatch Cloud) v1.0.0.
Use cases
- Fetch incidents
- Block domains (Block list)
- Update alerts
Configure Cisco Secure Cloud Analytics (Stealthwatch Cloud) on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for Stealthwatch Cloud.
- Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance.
- Stealthwatch server URL
- Stealthwatch Cloud API key. Should be in the form of "ApiKey :<api_key>"
- Use system proxy settings
- Trust any certificate (not secure)
- Fetch incidents
- Incident type
- Click Test to validate the URLs, token, and connection.
Commands
You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details.
- Get information for an alert: sw-show-alert
- Update an alert: sw-update-alert
- Get a list of all alerts: sw-list-alerts
- Block a domain or IP: sw-block-domain-or-ip
- Unblock a domain: sw-unblock-domain
- Get a list of blocked domains: sw-list-blocked-domains
- Get a list of observations: sw-list-observations
- Get a list of sessions by session occurrence time: sw-list-sessions
1. Get information for an alert
Returns information about a specific alert by the alert ID.
Base Command
sw-show-alert
Input
| Argument Name | Description | Required |
|---|---|---|
| alertID | The id of the required alert | Required |
| addComments | Add comments information, can be long | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Stealthwatch.Alert.id | number | Alert ID |
| Stealthwatch.Alert.assigned_to | string | Alert assignee |
| Stealthwatch.Alert.obj_created | date | Alert creation date |
| Stealthwatch.Alert.last_modified | date | Alert last modification |
| Stealthwatch.Alert.resolved | boolean | Alert state |
| Stealthwatch.Alert.source_info.ips | string | IP of the alert's source |
| Stealthwatch.Alert.source_info.hostnames | string | Hostname of the alert's source |
Command Example
!sw-show-alert alertID=275
Context Example
{
"assigned_to": null,
"assigned_to_username": null,
"created": "2018-07-23T15:30:00Z",
"description": "Source has started a port scan on a device internal to your network.",
"hostname": "",
"id": 275,
"ips_when_created": [],
"last_modified": "2018-10-02T22:41:07.749868Z",
"merit": 3,
"natural_time": "1 month ago",
"obj_created": "2018-07-23T16:34:01.566717Z",
"priority": 2,
"publish_time": "2018-07-23T16:34:01.531458+00:00",
"resolved": true,
"resolved_time": "2018-11-17T05:00:01.458445Z",
"resolved_user": null,
"rules_matched": null,
"snooze_settings": null,
"source": 48858,
"source_info": {
"created": "2018-09-23T15:49:39.025415+00:00",
"hostnames": [],
"ips": [
"5.5.255.25"
],
"name": "test.com",
"namespace": "default"
},
"source_name": "5.5.255.25",
"source_params": {
"id": 48852,
"meta": "net-link",
"name": "test.com"
},
"tags": [],
"text": "Internal Port Scanner on 5.5.255.25",
"time": "2018-10-02T21:49:00Z",
"type": "Internal Port Scanner"
}
2. Update an alert
Updates an alert.
Base Command
sw-update-alert
Input
| Argument Name | Description | Required |
|---|---|---|
| alertID | The ID of the alert to update | Required |
| resolved | Set the resolved field to true and set the merit field to close an alert. merit can be 8 ("helpful") or 9 ("not helpful") | Optional |
| merit | Set the resolved field to true and set the merit field to close an alert. merit can be 8 ("helpful") or 9 ("not helpful") | Optional |
| tags | Tags (string) | Optional |
| new_comment | Set the new_comment field to add a comment to the alert | Optional |
| publish_time | Publish time (string), e.g., publish_time=2018-08-01T07:54:39Z | Optional |
| snooze_settings | Snooze settings (string) | Optional |
| resolved_user | Username (string) | Optional |
| assigned_to | Assigned to (integer) | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Stealthwatch.Alert.id | number | Alert ID |
| Stealthwatch.Alert.assigned_to | string | Alert assignee |
| Stealthwatch.Alert.obj_created | date | Alert creation date |
| Stealthwatch.Alert.last_modified | date | Date the alert was last modified |
| Stealthwatch.Alert.resolved | boolean | Alert state |
| Stealthwatch.Alert.source_info.ips | string | IP of the alert's source |
| Stealthwatch.Alert.source_info.hostname | string | Hostname of the alert's source |
Command Example
!sw-update-alert alertID=275 merit=8 tags=test
3. Get a list of all alerts
Get the list of Stealthwatch alerts.
Base Command
sw-list-alerts
Input
| Argument Name | Description | Required |
|---|---|---|
| status | Filters alerts by status: open, closed, or all. Default is open. The all status enables you to see an individual alert whether it is open or closed. | Optional |
| search | Finds a particular string in the alerts, e.g., a particular IP address, hostname, or alert type. | Optional |
| assignee | Filter to only display alerts assigned to a specific user | Optional |
| tags | Tags shows alerts that are assigned a particular incident tag | Optional |
| limit | Number of alerts to list, default is 5 | Optional |
| addComments | Add comment to an alert, long-text supported | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Stealthwatch.Alert.id | number | Alert ID |
| Stealthwatch.Alert.assigned_to | string | Alert assignee |
| Stealthwatch.Alert.obj_created | date | Alert creation date |
| Stealthwatch.Alert.last_modified | date | Date the alert was last modified |
| Stealthwatch.Alert.resolved | boolean | Alert state |
| Stealthwatch.Alert.source_info.ips | string | IP of the alert's source |
| Stealthwatch.Alert.source_info.hostname | string | Hostname of the alert's source |
Command Example
{
"assigned_to": null,
"assigned_to_username": null,
"created": "2018-07-23T15:30:00Z",
"description": "Source has started a port scan on a device internal to your network.",
"hostname": "",
"id": 275,
"ips_when_created": [],
"last_modified": "2018-10-02T22:41:07.749868Z",
"merit": 3,
"natural_time": "1 month ago",
"obj_created": "2018-07-23T16:34:01.566717Z",
"priority": 2,
"publish_time": "2018-07-23T16:34:01.531458+00:00",
"resolved": true,
"resolved_time": "2018-11-17T05:00:01.458445Z",
"resolved_user": null,
"rules_matched": null,
"snooze_settings": null,
"source": 48858,
"source_info": {
"created": "2018-09-23T15:49:39.025415+00:00",
"hostnames": [],
"ips": [
"5.5.255.25"
],
"name": "test.com",
"namespace": "default"
},
"source_name": "5.5.255.25",
"source_params": {
"id": 48852,
"meta": "net-link",
"name": "test.com"
},
"tags": [],
"text": "Internal Port Scanner on 5.5.255.25",
"time": "2018-10-02T21:49:00Z",
"type": "Internal Port Scanner"
}
4. Block a domain or IP
Adds a domain or IP to the block list.
Base Command
sw-block-domain-or-ip
Input
| Argument Name | Description | Required |
|---|---|---|
| domain | Domain to add to the block list | Optional |
| ip | IP to add to the block list | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Stealthwatch.Domain.identifier | string | Domain name |
| Stealthwatch.Domain.title | string | Domain title |
| Stealthwatch.Domain.id | number | Domain ID |
| Stealthwatch.IP.identifier | string | IP address |
| Stealthwatch.IP.title | string | IP title |
| Stealthwatch.IP.id | string | IP ID |
Command Example
!sw-block-domain-or-ip domain=test.com
5. Unblock a domain
Removes a domain from the block list.
Base Command
sw-unblock-domain
Input
| Argument Name | Description | Required |
|---|---|---|
| id | ID of the domain to remove from the block list. You can find the id by running the sw-list-blocked-domains command. | True |
Context Output
There is no context output for this command.
Command Example
!sw-unblock-domain id=5
6. Get a list of blocked domains
Returns a list of blocked domains.
Base Command
sw-list-blocked-domains
Input
| Argument Name | Description | Required |
|---|---|---|
| search | Finds a particular string in the alerts, e.g., a particular IP address, hostname, or alert type. | Optional |
| domain | Search for a specific domain | Optional |
| limit | Number of domains to list, default is 5 | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Stealthwatch.Domain.identifier | string | Domain name |
| Stealthwatch.Domain.title | string | Domain title |
| Stealthwatch.Domain.id | number | Domain ID |
Command Example
!sw-list-blocked-domains limit=5
7. Get a list of observations
Returns observations by alert ID, observation ID, or a free search.
Base Command
sw-list-observations
Input
| Argument Name | Description | Required |
|---|---|---|
| search | Finds a particular string amongst the alerts. For example, a particular IP address, hostname, or alert type. | False |
| alert | Use the alert query parameter with an alert id to only show observations referenced by the alert | False |
| id | Get a specific observation by its ID | False |
| limit | Amount of observations to list. Default is 5 | False |
Context Output
| Path | Type | Description |
|---|---|---|
| Stealthwatch.Observation.id | number | Observation ID |
| Stealthwatch.Observation.port_count | number | Observation port count |
| Stealthwatch.Observation.creation_time | string | Observation creation time |
| Stealthwatch.Observation.end_time | string | Observation end time |
| Stealthwatch.Observation.scanned_ip | string | Observation scanned ip |
| Stealthwatch.Observation.scanner_ip | string | Observation scanner ip |
| Stealthwatch.Observation.source | unknown | Observation source |
Command Example
!sw-list-observations alert=222
Context Example
{
"cidr_range": "5.5.5.179/32",
"connected_ip": null,
"connected_ip_country_code": "",
"creation_time": "2018-07-23T15:30:00Z",
"end_time": "2018-07-23T15:30:00Z",
"id": 12345,
"observation_name": "Port Scanner",
"port_count": 24,
"port_ranges": "0-1023",
"resource_name": "port_scanner_v1",
"scan_type": "internal",
"scanned_packets": 5,
"scanner_packets": 75,
"source": 48822,
"time": "2018-07-23T15:30:00Z"
}
8. Get a list of sessions by session occurrence time
Get sessions by the session's occurrence time ( Time format: YYYY-MM-DDTHH:MM:SSZ)
Base Command
sw-list-sessions
Input
| Argument Name | Description | Required |
|---|---|---|
| startTime | Session start time (UTC), e.g., startTime="2018-09-30T12:00:00Z" | Required |
| endTime | Session end time (UTC), e.g., endTime="2018-07-31T15:00:00Z" | Optional |
| limit | Number of observations to list, default is 400 | Optional |
| ip | Source IP address to filter by | Optional |
| connectedIP | Connected IP to filter by | Optional |
| connectedDeviceId | Connected device ID | Optional |
| sessionType | Type of session - select external/internal to receive data only about this type of session | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Stealthwatch.Session.id | number | Session ID |
| Stealthwatch.Session.port | number | Session port |
| Stealthwatch.Session.start_timestamp_utc | string | Session start time |
| Stealthwatch.Session.ip | string | Session IP |
| Stealthwatch.Session.connected_ip | string | Session connected IP |
| Stealthwatch.Session.device_id | number | Source device ID |
| Stealthwatch.Session.connected_device_id | number | Connected device ID |
| Stealthwatch.Session.connected_device_is_external | boolean | Is the connected device external |
Command Example
!sw-list-sessions startTime="2018-10-30T12:00:00Z" endTime="2018-11-01T12:00:00Z"
Configuration parameters
serverURL— Stealthwatch's server URL (required)APIKey— Stealthwatch Cloud API key. Should be in the form of "ApiKey <username>:<apikey>"credentials_api_key—proxy— Use system proxy settingsinsecure— Trust any certificate (not secure)isFetch— Fetch incidentsincidentType— Incident typeincidentFetchInterval— Incidents Fetch Interval
Commands (8)
-
sw-block-domain-or-ipAdd a domain or IP to the block list
-
sw-list-alertsGet the list of Stealthwatch alerts
-
sw-list-blocked-domainsReturns list of the blocked domains
-
sw-list-observationsGet observations by alert ID, observation ID, or free search
-
sw-list-sessionsGet sessions by the session's occurrence time ( Time format: YYYY-MM-DDTHH:MM:SSZ)
-
sw-show-alertGet info about a specific alert by its ID
-
sw-unblock-domainRemove a domain from the block list
-
sw-update-alertUpdate an alert
import demistomock as demisto from CommonServerPython import * """ IMPORTS """ import collections import json import os from datetime import datetime, timedelta import requests import urllib3 urllib3.disable_warnings() """ GLOBAL VARS """ SERVER = demisto.params().get("serverURL", "").strip("/") SERVER_URL = SERVER + "/api/v3" API_KEY = demisto.params().get("credentials_api_key", {}).get("password") or demisto.params().get("APIKey") USE_SSL = not demisto.params().get("insecure") DEFAULT_HEADERS = {"Content-Type": "application/json", "Accept": "application/json", "Authorization": API_KEY} """ HELPER FUNCTIONS """ if not demisto.params().get("proxy"): # Remove proxy environment variables if they exist for proxy_var in ["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy"]: os.environ.pop(proxy_var, None) def http_request(method, url_suffix, params_dict=None, headers=DEFAULT_HEADERS, data=None): req_params = {} # type: Dict[Any,Any] if params_dict is not None: req_params.update(params_dict) url = SERVER_URL + url_suffix LOG(f"running {method} request with url={url}\tparams={json.dumps(req_params)}") try: res = requests.request(method, url, verify=USE_SSL, params=req_params, headers=headers, data=data) res.raise_for_status() try: return res.json() except ValueError: # in case the response doesn't have JSON return "Request completed" except Exception as e: LOG(e) raise (e) def underscore_to_camelcase(word): return " ".join(x.capitalize() or "_" for x in word.split("_")) def create_incident_data_from_alert(alert): alert.pop("comments") alert.pop("observations") return { "name": "Stealthwatch alert " + str(alert.get("id", "")), "rawJSON": json.dumps(alert), "occurred": alert.get("created", ""), } def get_latest_id(alerts_data): latest_id = 0 for alert in alerts_data: current_id = alert.get("id", None) if current_id is not None and current_id > latest_id: latest_id = current_id return latest_id """ COMMANDS FUNCTIONS """ def show_alert(alert_id): """ Returns alert by specific id """ api_endpoint = f"/alerts/alert/{alert_id}/" return http_request("GET", api_endpoint, {}, DEFAULT_HEADERS) def show_alert_command(): """ corresponds to 'sw-show-alert' command. Returns information about a specific alert """ alert_id = demisto.args().get("alertID") alert_data = show_alert(alert_id) if demisto.args().get("addComments", False) != "true": alert_data.pop("comments") alert_data.pop("new_comment") alert_data.pop("observations") list_for_md = ["resolved", "id", "last_modified", "obj_created", "assigned_to"] dict_for_md = {underscore_to_camelcase(k): v for k, v in alert_data.items() if k in list_for_md} md = tableToMarkdown(alert_data.get("text", ""), dict_for_md) return { "Type": entryTypes["note"], "Contents": alert_data, "ContentsFormat": formats["json"], "ReadableContentsFormat": formats["markdown"], "HumanReadable": md, "EntryContext": {"Stealthwatch.Alert(val.id==obj.id)": alert_data}, } def update_alert(alert_id, data): """ Updates alert by specific id """ api_endpoint = f"/alerts/alert/{alert_id}/" return http_request("PUT", api_endpoint, data=json.dumps(data)) def update_alert_command(): """ corresponds to 'sw-update-alert' command. Returns information about a specific alert """ args = demisto.args() alert_id = args.get("alertID") update_params = {} # adding the possible params for update possible_params = ["new_comment", "tags", "publish_time", "resolved", "snooze_settings", "merit", "assigned_to"] for param in possible_params: current_param = args.get(param, False) if current_param: update_params[param] = current_param username = args.get("resolved_user", None) if username is not None: update_params["resolved_user"] = {"username": username} alert_data = update_alert(alert_id, update_params) alert_data.pop("comments") alert_data.pop("new_comment") alert_data.pop("observations") list_for_md = ["resolved", "id", "last_modified", "obj_created", "assigned_to"] dict_for_md = {k: v for k, v in alert_data.items() if k in list_for_md} md = tableToMarkdown(alert_data.get("text", ""), dict_for_md) return { "Type": entryTypes["note"], "Contents": alert_data, "ContentsFormat": formats["json"], "ReadableContentsFormat": formats["markdown"], "HumanReadable": md, "EntryContext": {"Stealthwatch.Alert(val.id==obj.id)": alert_data}, } def list_alerts(params): """ Retrieves alerts """ api_endpoint = "/alerts/alert/" return http_request("GET", api_endpoint, params, DEFAULT_HEADERS) def build_alert_dic(alert): dic = collections.OrderedDict() # type: Dict[str,str] list_for_md = ["id", "last_modified", "resolved", "text", "obj_created", "assigned_to", "description"] for item in list_for_md: dic[underscore_to_camelcase(item)] = alert[item] return dic def list_alerts_command(): """ corresponds to 'sw-list-alerts' command. Returns a list of Stealthwatch alerts """ args = demisto.args() list_params = {} # adding the possible params for update possible_params = ["status", "tags", "search", "assignee", "limit"] for param in possible_params: current_param = args.get(param, False) if current_param: list_params[param] = current_param alerts_data = list_alerts(list_params).get("objects") md_dicts_list = [] for alert in alerts_data: if demisto.args().get("addComments", False) != "true": alert.pop("comments") alert.pop("new_comment") alert.pop("observations") md_dicts_list.append(build_alert_dic(alert)) md = tableToMarkdown("The following alerts were retrieved", md_dicts_list) return { "Type": entryTypes["note"], "Contents": alerts_data, "ContentsFormat": formats["json"], "ReadableContentsFormat": formats["markdown"], "HumanReadable": md, "EntryContext": {"Stealthwatch.Alert(val.id==obj.id)": alerts_data}, } def domain_block(params): """ Updates domain blacklist status """ api_endpoint = "/blacklist/domains/" return http_request("POST", api_endpoint, {}, DEFAULT_HEADERS, params) def block_domain_command(): """ corresponds to 'sw-block-domain-or-ip' command. Adds a domain to the blacklist """ domain = demisto.args().get("domain") ip = demisto.args().get("ip") if not (domain or ip): return {"Type": entryTypes["error"], "ContentsFormat": formats["text"], "Contents": "Please enter either domain or ip"} if domain and ip: return { "Type": entryTypes["error"], "ContentsFormat": formats["text"], "Contents": "Please enter only domain or ip, not both", } identifier = None if domain: identifier = domain else: identifier = ip domain_params = {"identifier": identifier, "category": "domain", "list_on": "blacklist"} domain_result = domain_block(json.dumps(domain_params)) ec = None if domain: ec = {"Stealthwatch.Domain(val.identifier==obj.identifier)": domain_result} else: ec = {"Stealthwatch.IP(val.identifier==obj.identifier)": domain_result} return { "Type": entryTypes["note"], "Contents": domain_result, "ContentsFormat": formats["json"], "ReadableContentsFormat": formats["markdown"], "HumanReadable": tableToMarkdown("Blacklist " + domain + " result", domain_result), "EntryContext": ec, } def domain_unblock(domain_id): """ Removes domain from the blacklist """ api_endpoint = f"/blacklist/domains/{domain_id}/" return http_request("DELETE", api_endpoint, None, DEFAULT_HEADERS, None) def unblock_domain_command(): """ corresponds to 'sw-unblock-domain' command. Removes a domain to the blacklist """ domain_id = demisto.args().get("id") domain_result = domain_unblock(domain_id) return { "Type": entryTypes["note"], "Contents": domain_result, "ContentsFormat": formats["json"], "ReadableContentsFormat": formats["markdown"], "HumanReadable": "Unblocked domain with id: " + domain_id, } def list_domains(list_params): """ Lists blacklisted domains """ api_endpoint = "/blacklist/domains/" return http_request("GET", api_endpoint, list_params, DEFAULT_HEADERS, {}) def list_blocked_domains_command(): """ corresponds to 'sw-list-blocked-domains' command. Returns a list of the blocked domains """ args = demisto.args() list_params = {} # adding the possible params for update possible_params = ["search", "limit"] for param in possible_params: current_param = args.get(param, False) if current_param: list_params[param] = current_param specific_domain = args.get("domain", None) if specific_domain is not None: list_params["identifier"] = specific_domain domains_data = list_domains(list_params) domains_result = domains_data.get("objects", {}) data_output = [] for obs in domains_result: data_output.append({underscore_to_camelcase(k): v for k, v in list(obs.items())}) return { "Type": entryTypes["note"], "Contents": domains_data, "ContentsFormat": formats["json"], "ReadableContentsFormat": formats["markdown"], "HumanReadable": tableToMarkdown("Current blacklisted domains are", data_output), "EntryContext": {"Stealthwatch.Domain(val.identifier==obj.identifier)": domains_result}, } def list_observations(params): """ Lists observations """ api_endpoint = "/observations/all/" return http_request("GET", api_endpoint, params, DEFAULT_HEADERS) def list_observations_command(): """ corresponds to 'sw-list-observations' command. Returns a list of Stealthwatch observations """ args = demisto.args() list_params = {"ordering": "creation_time"} # adding the possible params for update possible_params = ["alert", "id", "search", "limit"] for param in possible_params: current_param = args.get(param, False) if current_param: list_params[param] = current_param observations_data = list_observations(list_params).get("objects") data_output = [] for obs in observations_data: data_output.append({underscore_to_camelcase(k): v for k, v in list(obs.items())}) return { "Type": entryTypes["note"], "Contents": data_output, "ContentsFormat": formats["json"], "ReadableContentsFormat": formats["markdown"], "HumanReadable": tableToMarkdown("Found the following observations", data_output), "EntryContext": {"Stealthwatch.Observation(val.id==obj.id)": observations_data}, } def list_sessions(params): """ Lists observations """ api_endpoint = "/snapshots/session-data/" return http_request("GET", api_endpoint, params, DEFAULT_HEADERS) def list_sessions_command(): """ corresponds to 'sw-list-sessions' command. Returns a list of Stealthwatch sessions """ date_format = "%Y-%m-%dT%H:%M:%SZ" list_params = {} ip = demisto.args().get("ip") connected_ip = demisto.args().get("connectedIP") connected_device_id = demisto.args().get("connectedDeviceId") limit = demisto.args().get("limit") start_time = demisto.args().get("startTime", None) end_time = demisto.args().get("endTime", None) session_type = demisto.args().get("sessionType", "all") if start_time and end_time: list_params["start_datetime"] = start_time list_params["end_datetime"] = end_time elif end_time is None: start_time_object = datetime.strptime(start_time, date_format) start_time_object = start_time_object - timedelta(minutes=5) end_time_object = start_time_object + timedelta(minutes=5) start_time = start_time_object.strftime(date_format) end_time = end_time_object.strftime(date_format) list_params["ip"] = ip list_params["connected_ip"] = connected_ip list_params["limit"] = limit list_params["start_datetime"] = start_time list_params["end_datetime"] = end_time list_params["connected_device_id"] = connected_device_id unique_session_ids = [] # type: List[str] final_sessions_data = [] sessions_data = list_sessions(list_params).get("objects") for sess in sessions_data: if sess["connected_ip"] not in unique_session_ids: unique_session_ids.append(sess["connected_ip"]) if demisto.get(sess, "connected_device_id"): sess["connected_device_is_external"] = False if session_type == "internal": final_sessions_data.append(sess) else: sess["connected_device_is_external"] = True if session_type == "external": final_sessions_data.append(sess) if session_type == "all": final_sessions_data.append(sess) data_output = [] for sess in final_sessions_data: data_output.append({underscore_to_camelcase(k): v for k, v in list(sess.items())}) return { "Type": entryTypes["note"], "Contents": data_output, "ContentsFormat": formats["json"], "ReadableContentsFormat": formats["markdown"], "HumanReadable": tableToMarkdown("Found the following session data", data_output), "EntryContext": {"Stealthwatch.Session(val.id==obj.id)": final_sessions_data}, } def fetch_incidents(): date_format = "%Y-%m-%dT%H:%M:%SZ" list_params = {"ordering": "created", "limit": 100} final_alerts = [] last_fetch_string = demisto.getLastRun().get("last_fetch_time", None) ids = demisto.getLastRun().get("ids", None) first_time = not last_fetch_string and ids is not None if last_fetch_string is None or not last_fetch_string: now = datetime.now() last_fetch = now - timedelta(days=20) else: last_fetch = parse_date_string(last_fetch_string) # Couldn't find a way to sort descending so looking for last offset of 100 alerts alerts_response = list_alerts(list_params) num_alerts = alerts_response.get("meta", {"total_count": 100}).get("total_count") offset = 0 if num_alerts < 100 else num_alerts - 100 list_params["offset"] = offset alerts_response = list_alerts(list_params) alerts_data = alerts_response.get("objects", []) max_fetch_time = last_fetch_string if last_fetch_string else now.strftime(date_format) for alert in alerts_data: created = alert.get("created") if parse_date_string(created) > last_fetch: incident_from_alert = create_incident_data_from_alert(alert) if first_time: if alert.get("id") not in ids: final_alerts.append(incident_from_alert) else: final_alerts.append(incident_from_alert) if parse_date_string(created) > parse_date_string(max_fetch_time): max_fetch_time = created demisto.setLastRun({"last_fetch_time": max_fetch_time}) demisto.incidents(final_alerts) """ EXECUTION CODE """ def main(): demisto.debug(f"Command being called is {demisto.command()}") if not API_KEY: raise DemistoException("Stealthwatch Cloud API key must be provided.") try: if demisto.command() == "test-module": # This is the call made when pressing the integration test button. if list_alerts_command(): demisto.results("ok") else: demisto.results("test failed") elif demisto.command() == "sw-show-alert": demisto.results(show_alert_command()) elif demisto.command() == "sw-update-alert": demisto.results(update_alert_command()) elif demisto.command() == "sw-list-alerts": demisto.results(list_alerts_command()) elif demisto.command() == "sw-block-domain-or-ip": demisto.results(block_domain_command()) elif demisto.command() == "sw-unblock-domain": demisto.results(unblock_domain_command()) elif demisto.command() == "sw-list-blocked-domains": demisto.results(list_blocked_domains_command()) elif demisto.command() == "sw-list-observations": demisto.results(list_observations_command()) elif demisto.command() == "sw-list-sessions": demisto.results(list_sessions_command()) elif demisto.command() == "fetch-incidents": demisto.results(fetch_incidents()) except Exception as e: return_error(f"error has occurred: {e!s}") if __name__ in ("__main__", "__builtin__", "builtins"): # pragma: no cover main()