Skyhigh Security
Skyhigh Security is a cloud-based, multi-tenant service that enables Cloud Discovery and Risk Monitoring, Cloud Usage Analytics, Cloud Access and Control.
Network Security · Skyhigh Security SSE
Details
| ID | Skyhigh Security |
|---|---|
| Provider | STG |
| Category | Network Security |
| From Version | 6.5.0 |
| Docker Image | demisto/python3:3.12.13.10404775 |
| Supported Modules | Agentix XSIAM |
README
Additional information
Skyhigh Security is a cloud-based, multi-tenant service that enables Cloud Discovery and Risk Monitoring, Cloud Usage Analytics, Cloud Access and Control.
This integration was integrated and tested with version 1 of Skyhigh Security.
API limitations
Do to API limitations, keep in mind that over time the integration can start to work more slowly.
The solution is to restart the last-run.
Configure Skyhigh Security in Cortex
| Parameter | Description | Required |
|---|---|---|
| Base URL (e.g., https://www.myshn.net) | True | |
| Password | The username and password to use for the connection | True |
| Maximum number of incidents to fetch every time. Default is 50. Maximum is 500. | False | |
| First fetch in timestamp format (<number> <time unit>, e.g., 12 hours, 7 days). Default is 3 days. | False | |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False |
Commands
You can execute these commands from the 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.
skyhigh-security-incident-query
Retrieves a list of incidents in ascending time modified order.
Base Command
skyhigh-security-incident-query
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | Maximum number of items that will be returned within a single response. Maximum is 500. If the limit value exceeds the 500 maximum, it will not be flagged as an error but will also not increase results. Default is 50. | Optional |
| page | Pagination support for use with a large “limit” value. | Optional |
| page_size | Pagination support for use with a large “limit” value. The maximum is 500. | Optional |
| start_time | For time arguments use the ISO-8601 standard - ‘%Y-%m-%dT%H:%M:%SZ’ or relative time (last X days). Default is 3 days. | Optional |
| end_time | For time arguments use the ISO-8601 standard - ‘%Y-%m-%dT%H:%M:%SZ’ or relative time (last X days). | Optional |
| actor_ids | The actor IDs of the incidents to retrieve. | Optional |
| service_names | The service names of the incidents to retrieve. | Optional |
| incident_types | The type of the incidents to retrieve. Possible values are: Alert, Threat. | Optional |
| categories | The categories of the incidents to retrieve. When defining the categories argument the incident_types argument is ignored. Possible values are: Access, Admin, Audit, CompromisedAccount, Data, InsiderThreat, Policy, PrivilegeAccess, Vulnerability. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SkyhighSecurity.Incident | Unknown | The incident’s metadata. |
Command example
!skyhigh-security-incident-query limit="3" start_time="3 days"
Context Example
{
"SkyhighSecurity": {
"Incident": [
{
"activityNames": [],
"actorId": "NOT AVAILABLE",
"actorIdType": "USER",
"incidentGroup": "Alert.Policy.CloudAccess",
"incidentGroupId": null,
"incidentId": "CAP-111111",
"incidentRiskScore": 10,
"incidentRiskSeverity": "high",
"incidentRiskSeverityId": 2,
"information": {
"contentItemCreatedOn": "2022-07-01T19:13:53.075Z",
"contentItemId": "UNKNOWN",
"contentItemName": "/css/webfonts/office365icons.eot?",
"contentItemType": "REQUEST",
"device": {
"application": {
"type": "application",
"user_agent": "Chrome"
},
"device_type": "Desktop",
"ip": "1.1.1.1",
"os": {
"platform": "Windows",
"type": "os"
}
},
"eventId": "222222",
"lastExecutedResponseLabel": "Block Access",
"policyId": 333333,
"policyName": "Block downloads to unmanaged devices",
"userAttributes": {}
},
"instanceId": 444444,
"instanceName": "zengel",
"responses": ["Block Access"],
"serviceNames": ["Microsoft Office 365 and OneDrive"],
"significantlyUpdatedAt": "2022-07-01T19:13:57.053Z",
"status": "new",
"timeCreated": "2022-07-01T19:13:53.075Z",
"timeModified": "2022-07-01T19:13:57.053Z"
},
{
"activityNames": [],
"actorId": "NOT AVAILABLE",
"actorIdType": "USER",
"incidentGroup": "Alert.Policy.CloudAccess",
"incidentGroupId": null,
"incidentId": "CAP-555555",
"incidentRiskScore": 3,
"incidentRiskSeverity": "low",
"incidentRiskSeverityId": 0,
"information": {
"contentItemCreatedOn": "2022-07-02T02:38:16.706Z",
"contentItemId": "UNKNOWN",
"contentItemName": "/",
"contentItemType": "REQUEST",
"device": {
"application": {
"type": "application",
"user_agent": "Unknown"
},
"device_type": "Unknown",
"ip": "2.2.2.2",
"os": {
"platform": "Unknown",
"type": "os"
}
},
"eventId": "666666",
"lastExecutedResponseLabel": "Allow Access",
"policyId": 777777,
"policyName": "allow successfactors",
"userAttributes": {}
},
"instanceId": 888888,
"instanceName": "ZengelBiz",
"responses": ["Allow Access"],
"serviceNames": ["SAP - SuccessFactors HXM Suite"],
"significantlyUpdatedAt": "2022-07-02T02:38:18.682Z",
"status": "new",
"timeCreated": "2022-07-02T02:38:16.706Z",
"timeModified": "2022-07-02T02:38:18.682Z"
},
{
"activityNames": [],
"actorId": "NOT AVAILABLE",
"actorIdType": "USER",
"incidentGroup": "Alert.Policy.CloudAccess",
"incidentGroupId": null,
"incidentId": "CAP-999999",
"incidentRiskScore": 3,
"incidentRiskSeverity": "low",
"incidentRiskSeverityId": 0,
"information": {
"contentItemCreatedOn": "2022-07-02T02:38:16.888Z",
"contentItemId": "UNKNOWN",
"contentItemName": "/",
"contentItemType": "REQUEST",
"device": {
"application": {
"type": "application",
"user_agent": "Unknown"
},
"device_type": "Unknown",
"ip": "2.2.2.2",
"os": {
"platform": "Unknown",
"type": "os"
}
},
"eventId": "144304",
"lastExecutedResponseLabel": "Allow Access",
"policyId": 777777,
"policyName": "allow successfactors",
"userAttributes": {}
},
"instanceId": 888888,
"instanceName": "ZengelBiz",
"responses": ["Allow Access"],
"serviceNames": ["SAP - SuccessFactors HXM Suite"],
"significantlyUpdatedAt": "2022-07-02T02:38:19.202Z",
"status": "new",
"timeCreated": "2022-07-02T02:38:16.888Z",
"timeModified": "2022-07-02T02:38:19.202Z"
}
]
}
}
Human Readable Output
Skyhigh Security Incidents
Alert Severity Incident ID Service Name Status Time (UTC) User Name high CAP-111111 Microsoft Office 365 and OneDrive new 2022-07-01T19:13:53.075Z NOT AVAILABLE low CAP-555555 SAP - SuccessFactors HXM Suite new 2022-07-02T02:38:16.706Z NOT AVAILABLE low CAP-999999 SAP - SuccessFactors HXM Suite new 2022-07-02T02:38:16.888Z NOT AVAILABLE
skyhigh-security-incident-status-update
Update status of single/multiple incidents.
Note!
For multiple IDs, a single status will be applied for all IDs
e.g., 123, 456, 789 » change status to » closed.
Base Command
skyhigh-security-incident-status-update
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_ids | The incidents IDs that should be updated. | Required |
| status | The new status of the incidents. Possible values are: new, opened, false positive, resolved, suppressed, archived. | Required |
Context Output
There is no context output for this command.
Command example
!skyhigh-security-incident-status-update incident_ids=CAP-114044 status=archived
Human Readable Output
Status updated for user
skyhigh-security-anomaly-activity-list
Fetches activities for a given anomaly ID.
Base Command
skyhigh-security-anomaly-activity-list
Input
| Argument Name | Description | Required |
|---|---|---|
| anomaly_id | The anomaly ID from where to retrieve the activities. Only for incidents of type anomaly (ANO-123). | Required |
Context Output
| Path | Type | Description | |
|---|---|---|---|
| SkyhighSecurity.AnomalyActivity.timeStamp | String | The timestamp of the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.actionName | String | The action name. | |
| SkyhighSecurity.AnomalyActivity.asnName | String | The ASN name of an activity. | |
| SkyhighSecurity.AnomalyActivity.city | String | The city where the anomaly activity occurred. | |
| SkyhighSecurity.AnomalyActivity.collabGroup | String | The collaboration group for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.count | Number | The number of anomalies detected. | |
| SkyhighSecurity.AnomalyActivity.country | String | The country of the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.deviceManaged | Boolean | Whether the anomaly activity is managed by the device or not. | |
| SkyhighSecurity.AnomalyActivity.directory | String | The directory of the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.downloadBytes | Number | The number of bytes downloaded by the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.eventCount | Number | The number of anomalies detected. | |
| SkyhighSecurity.AnomalyActivity.fileFolderPath | String | The file folder path for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.fileName | String | The file name of the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.fileSharingEnabled | Boolean | Whether the CASB file sharing is enabled or not. | |
| SkyhighSecurity.AnomalyActivity.fileSize | Number | The file size of the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.fileType | String | The file type of the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.geoOrgNameV1 | String | The geo organization name. | |
| SkyhighSecurity.AnomalyActivity.httpMethod | String | The HTTP method used by the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.instanceId | String | The instance ID for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.isSourceTrusted | Boolean | Whether the anomaly activity source is trusted or not. | |
| SkyhighSecurity.AnomalyActivity.networkType | String | The network type for the anomaly. | |
| SkyhighSecurity.AnomalyActivity.objectType | String | The object type for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.operation | String | The operation type. | |
| SkyhighSecurity.AnomalyActivity.proxyDescription | String | The proxy description for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.proxyType | String | The proxy type for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.region | String | The region where the anomaly activity occurred. | |
| SkyhighSecurity.AnomalyActivity.serviceName | String | The name of the service. | |
| SkyhighSecurity.AnomalyActivity.siteUrl | String | The URL of the CASB’s site. | |
| SkyhighSecurity.AnomalyActivity.sourceIP | IP | The IP address of the source IP. | |
| SkyhighSecurity.AnomalyActivity.sourceIdentifier | String | The source identifier for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.targetId | String | The target ID for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.targetType | String | The anomaly activity type. | |
| SkyhighSecurity.AnomalyActivity.tenantId | Number | The tenant ID for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.threatCategory | String | The threat category for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.trustEntity | String | The trust entity for the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.trustReason | String | The trust reason of the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.uploadBytes | Number | The number of bytes uploaded. | |
| SkyhighSecurity.AnomalyActivity.url | String | The URL of the anomaly activity. | |
| SkyhighSecurity.AnomalyActivity.user | String | The user who triggered the anomaly. |
skyhigh-security-policy-dictionary-list
List existing policy dictionaries.
Base Command
skyhigh-security-policy-dictionary-list
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | Maximum number of policies that will be returned within a single response. Default is 50. | Optional |
| page | Pagination support for use with a large “limit” value. | Optional |
| page_size | Pagination support for use with a large “limit” value. | Optional |
| name | The name of the policies to retrieve. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SkyhighSecurity.Dictionaries.ID | Number | The ID for the dictionary. |
| SkyhighSecurity.Dictionaries.LastModified | String | The date the dictionary was last modified. |
| SkyhighSecurity.Dictionaries.Name | String | The name of the dictionary. |
Command example
!skyhigh-security-policy-dictionary-list limit="3"
Context Example
{
"SkyhighSecurity": {
"dictionaries": [
{
"ID": 121212,
"LastModified": "2022-07-04T14:02:03.000+0000",
"Name": "(Default) Internal Domains"
},
{
"ID": 131313,
"LastModified": "2020-04-15T13:08:09.000+0000",
"Name": "Access Whitelist Users"
},
{
"ID": 141414,
"LastModified": "2021-07-14T12:22:37.000+0000",
"Name": "Allowed Geo"
}
]
}
}
Human Readable Output
List of Skyhigh Security Policies
ID Last Modified Name 121212 2022-07-04T14:02:03.000+0000 (Default) Internal Domains 131313 2020-04-15T13:08:09.000+0000 Access Whitelist Users 141414 2021-07-14T12:22:37.000+0000 Allowed Geo
skyhigh-security-policy-dictionary-update
Adds new content to an existing policy dictionary.
Base Command
skyhigh-security-policy-dictionary-update
Input
| Argument Name | Description | Required |
|---|---|---|
| dictionary_id | The dictionary where to set the policy. | Required |
| name | A name for the new key-value which will be added in the dictionary. | Required |
| content | The value to be set in the dictionary for the given key-name. Multiple values can be separated by commas. | Required |
Context Output
There is no context output for this command.
Command example
!skyhigh-security-policy-dictionary-update dictionary_id="121212" name="(Default) Internal Domains" content="gmail.com, outlook.com"
Human Readable Output
Dictionary id: 121212 was updated.
Configuration parameters
url— Base URL (e.g., https://www.myshn.net) (required)credentials— Credentials (required)max_fetch— Maximum number of incidents to fetch every time. Default is 50. Maximum is 500.first_fetch— First fetch in timestamp format (<number> <time unit>, e.g., 12 hours, 7 days). Default is 3 days.incidentType— Incident typeincidentFetchInterval— Incidents Fetch IntervalisFetch— Fetch incidentsinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (5)
-
skyhigh-security-anomaly-activity-listFetches activities for a given anomaly ID.
-
skyhigh-security-incident-queryRetrieves a list of incidents in ascending time modified order.
-
skyhigh-security-incident-status-updateUpdate status of single/multiple incidents. Note! For multiple IDs, a single status will be applied for all IDs e.g., 123, 456, 789 >> change status to >> closed.
-
skyhigh-security-policy-dictionary-listList existing policy dictionaries.
-
skyhigh-security-policy-dictionary-updateAdds new content to an existing policy dictionary.
import csv import io import traceback from typing import Any # Disable insecure warnings import urllib3 from CommonServerPython import * # noqa # pylint: disable=unused-wildcard-import from CommonServerUserPython import * # noqa urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) """ CONSTANTS """ DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" # ISO8601 format with UTC, default in XSOAR CategoryToIncidentType = { "Access": "Alert", "Admin": "Alert", "Audit": "Alert", "Data": "Alert", "Policy": "Alert", "Vulnerability": "Alert", "CompromisedAccount": "Threat", "InsiderThreat": "Threat", "PrivilegeAccess": "Threat", } """ CLIENT CLASS """ def csv2json(csv_data: str): """Converts data from csv to json Args: csv_data: data in csv format Returns: the same data in json formal """ reader = csv.DictReader(io.StringIO(csv_data)) json_data = list(reader) return json_data class Client(BaseClient): def test(self): self.incident_query(1, (arg_to_datetime("3 days") or datetime.now() - timedelta(days=3)).strftime(DATE_FORMAT)) def incident_query( self, limit: Optional[int], start_time: str = "", end_time: str = "", actor_ids: list[str] = None, service_names: list[str] = None, categories: list[str] = None, ) -> dict[str, Any]: url_suffix = "/external/api/v1/queryIncidents" params = {"limit": limit or 50} data = assign_params( startTime=start_time, endTime=end_time, actorIds=actor_ids, serviceNames=service_names, incidentCriteria=assign_params(categories=categories), ) return self._http_request("POST", url_suffix, params=params, json_data=data, raise_on_status=True) def status_update(self, incident_ids: List, status: str) -> dict[str, str]: url_suffix = "/external/api/v1/modifyIncidents" data = [{"incidentId": incident_id, "changeRequests": {"WORKFLOW_STATUS": status}} for incident_id in incident_ids] return self._http_request("POST", url_suffix, json_data=data, raise_on_status=True) def anomaly_activity_list(self, incident_id: Optional[int]) -> Optional[bytes]: url_suffix = "/external/api/v1/queryActivities" data = {"incident_id": incident_id} results = self._http_request("POST", url_suffix, json_data=data, resp_type="response") demisto.debug(f"This is the results from the activity list: {results}") activities = results.content demisto.debug(f"This is the content from the activity list: {activities}") return activities def policy_dictionary_list(self) -> List[dict]: url_suffix = "/dlp/dictionary" return self._http_request("GET", url_suffix, raise_on_status=True) def policy_dictionary_update(self, dict_id: Optional[int], name: str, content: str) -> dict[str, str]: url_suffix = "/dlp/dictionary" data = {"id": dict_id, "name": name, "content": content} return self._http_request("PUT", url_suffix, data=json.dumps(data), raise_on_status=True) """ HELPER FUNCTIONS """ def calculate_offset_and_limit(**kwargs) -> tuple[int, int]: if limit := arg_to_number(kwargs.get("limit")): # 'limit' is stronger than pagination ('page', and 'page_size'). return 0, limit if (page := arg_to_number(kwargs.get("page"))) and (page_size := arg_to_number(kwargs.get("page_size"))): page -= 1 # First page means list in index zero. return page * page_size, page * page_size + page_size return 0, 50 def convert_to_xsoar_severity(severity: str) -> float: """Maps a Skyhigh Security incident severity to a Cortex XSOAR severity. Known documented values ('low', 'medium', 'high'), as well as 'info', map to their XSOAR equivalents. The lookup is case-insensitive. Any unrecognized or missing value is mapped to IncidentSeverity.UNKNOWN. :type severity: ``str`` :param severity: severity as returned from the Skyhigh Security API. :return: Cortex XSOAR Severity. :rtype: ``float`` """ severity_map = { "info": IncidentSeverity.INFO, "low": IncidentSeverity.LOW, "medium": IncidentSeverity.MEDIUM, "high": IncidentSeverity.HIGH, } normalized = (severity or "").lower() if normalized not in severity_map: demisto.debug(f"Unmapped Skyhigh incidentRiskSeverity '{severity}' received; defaulting to UNKNOWN severity.") return IncidentSeverity.UNKNOWN return severity_map[normalized] """ COMMAND FUNCTIONS """ def test_module(client: Client) -> str: """Tests API connectivity and authentication' Returning 'ok' indicates that the integration works like it is supposed to. Connection to the service is successful. Raises exceptions if something goes wrong. type client: ``Client`` :param client: client to use :return: 'ok' if test passed, anything else will fail the test. :rtype: ``str`` """ try: client.test() message = "ok" except DemistoException as e: if "Forbidden" in str(e) or "Authorization" in str(e): message = "Authorization Error: make sure API Key is correctly set" else: raise return message def fetch_incidents(client: Client, params: dict) -> tuple[dict, list]: last_run = demisto.getLastRun() xsoar_incidents = [] limit = arg_to_number(params.get("max_fetch", 50)) if not (start_time := last_run.get("start_time")): # in the first interval. start_time = (arg_to_datetime(params.get("first_fetch", "3 days"))).strftime(DATE_FORMAT) # type: ignore[union-attr] result = client.incident_query(limit, start_time) if incidents := result.get("body", {}).get("incidents", []): ids = set(last_run.get("ids", set())) for incident in incidents: # Since the API returns the incidents in ascending time modified order. # As mentioned here: # https://success.myshn.net/Skyhigh_CASB/Skyhigh_CASB_APIs/Incidents_API/02_Incidents_API_Paths#_responses_3 # We need to verify no duplicates are pushed to xsoar. if (incident_id := incident.get("incidentId")) not in ids: xsoar_incidents.append( { "name": f"Skyhigh Security Incident {incident_id}", "occurred": incident.get("timeModified"), "rawJSON": json.dumps(incident), "dbotMirrorId": incident_id, "severity": convert_to_xsoar_severity(incident.get("incidentRiskSeverity", "low")), } ) ids.add(incident_id) last_run = {"start_time": result.get("body", {}).get("responseInfo", {}).get("nextStartTime", ""), "ids": list(ids)} return last_run, xsoar_incidents def incident_query_command(client: Client, args: dict) -> CommandResults: limit = arg_to_number(args.get("limit", 50)) start_time = (arg_to_datetime(args.get("start_time")) or datetime.now() - timedelta(days=3)).strftime(DATE_FORMAT) end_time = (arg_to_datetime(args.get("end_time")) or datetime.now()).strftime(DATE_FORMAT) actor_ids = argToList(args.get("actor_ids")) service_names = argToList(args.get("service_names")) if categories := argToList(args.get("categories")): categories = [{"incidentType": CategoryToIncidentType.get(category), "category": category} for category in categories] elif incident_types := argToList(args.get("incident_types")): categories = [{"incidentType": incident_type} for incident_type in incident_types] if not (page_number := arg_to_number(args.get("page_number"))) or not (page_size := arg_to_number(args.get("page_size"))): result = client.incident_query(limit, start_time, end_time, actor_ids, service_names, categories) else: result = {} for _ in range(page_number): result = client.incident_query(page_size, start_time, end_time, actor_ids, service_names, categories) start_time = result.get("body", {}).get("responseInfo", {}).get("nextStartTime", {}) if incidents := result.get("body", {}).get("incidents"): readable_dict = [] for incident in incidents: readable_dict.append( { "IncidentID": incident.get("incidentId"), "Time(UTC)": incident.get("timeCreated"), "Status": incident.get("status"), "Alert Action": incident.get("remediationResponse"), "Service Name": incident.get("serviceNames"), "Alert Severity": incident.get("incidentRiskSeverity"), "User Name": incident.get("actorId"), "Policy Name": incident.get("policyName"), } ) readable_output = tableToMarkdown( "Skyhigh Security Incidents", readable_dict, headerTransform=pascalToSpace, removeNull=True ) return CommandResults( outputs=incidents, outputs_prefix="SkyhighSecurity.Incident", outputs_key_field="incidentId", readable_output=readable_output, raw_response=incidents, ) else: return CommandResults( readable_output="No Incidents were found with the requested filters.", ) def status_update_command(client: Client, args: dict) -> CommandResults: incident_ids = argToList(args.get("incident_ids")) status = str(args.get("status")) result = client.status_update(incident_ids, status) readable_output = "Status updated for user" return CommandResults( readable_output=readable_output, raw_response=result, ) def anomaly_activity_list_command(client: Client, args: dict) -> CommandResults: anomaly_id = arg_to_number(args.get("anomaly_id")) result = client.anomaly_activity_list(anomaly_id) if not result: return CommandResults(readable_output="No activities found for anomaly ID " + str(anomaly_id)) anomaly_results = csv2json(result.decode("utf-8")) return CommandResults( outputs=result, outputs_prefix="SkyhighSecurity.Dictionaries", outputs_key_field="ID", readable_output=tableToMarkdown("Anomaly Activity List", anomaly_results), raw_response=result, ) def policy_dictionary_list_command(client: Client, args: dict) -> CommandResults: offset, limit = calculate_offset_and_limit(**args) names = argToList(args.get("name")) result = client.policy_dictionary_list() policies = result[offset:limit] filtered_policies = [] for policy in policies: if (names and policy.get("name") in names) or not names: filtered_policies.append( { "ID": policy.get("id"), "Name": policy.get("name"), "LastModified": policy.get("last_modified_time"), } ) readable_output = tableToMarkdown( "List of Skyhigh Security Policies", filtered_policies, headerTransform=pascalToSpace, removeNull=True ) return CommandResults( outputs=filtered_policies, outputs_prefix="SkyhighSecurity.Dictionaries", outputs_key_field="ID", readable_output=readable_output, raw_response=filtered_policies, ) def policy_dictionary_update_command(client: Client, args: dict) -> CommandResults: dict_id = arg_to_number(args.get("dictionary_id")) name = str(args.get("name")) content = str(args.get("content")) result = client.policy_dictionary_update(dict_id, name, content) return CommandResults(readable_output=f"Dictionary id: {dict_id} was updated.", raw_response=result) """ MAIN FUNCTION """ def main() -> None: """main function, parses params and runs command functions :return: :rtype: """ params = demisto.params() base_url = urljoin(params["url"].removesuffix("/"), "/shnapi/rest") verify_certificate = not params.get("insecure", False) credentials = params.get("credentials", {}) handle_proxy() command = demisto.command() demisto.debug(f"Command being called is {command}") try: commands: dict = { "skyhigh-security-incident-query": incident_query_command, "skyhigh-security-incident-status-update": status_update_command, "skyhigh-security-anomaly-activity-list": anomaly_activity_list_command, "skyhigh-security-policy-dictionary-list": policy_dictionary_list_command, "skyhigh-security-policy-dictionary-update": policy_dictionary_update_command, } client = Client( base_url=base_url, verify=verify_certificate, auth=(credentials.get("identifier"), credentials.get("password")), proxy=params.get("proxy"), ) if command == "test-module": result = test_module(client) return_results(result) if command == "fetch-incidents": last_run, incidents = fetch_incidents(client, params) demisto.setLastRun(last_run) demisto.incidents(incidents) elif command in commands: return_results(commands[command](client, demisto.args())) # Log exceptions and return errors except Exception as e: demisto.error(traceback.format_exc()) # print the traceback return_error(f"Failed to execute {command} command.\nError:\n{e!s}") """ ENTRY POINT """ if __name__ in ("__main__", "__builtin__", "builtins"): main()