From 3a3774f6947b8def7db385f113461195c6b0cd60 Mon Sep 17 00:00:00 2001 From: jang Date: Thu, 3 Jul 2025 16:33:09 -0400 Subject: [PATCH] Add three workflow actions for customer. get_workflow_definitions, execute_workflow, get_workflow_results. --- crowdstrikeoauthapi.json | 833 +++++++++++++++++++++++++++++++ crowdstrikeoauthapi_connector.py | 192 +++++++ crowdstrikeoauthapi_consts.py | 5 + 3 files changed, 1030 insertions(+) diff --git a/crowdstrikeoauthapi.json b/crowdstrikeoauthapi.json index 84a0cbc..1bdb09e 100644 --- a/crowdstrikeoauthapi.json +++ b/crowdstrikeoauthapi.json @@ -22961,6 +22961,839 @@ "type": "custom", "view": "crowdstrike_view.display_view" } + }, + { + "action": "get workflow definitions", + "identifier": "get_workflow_definitions", + "description": "Search workflow definitions based on the provided filter.", + "verbose": "", + "type": "investigate", + "read_only": true, + "parameters": { + "name": { + "description": "Builds a filter to search for a definition matching the provided value. '*' may used as a wildcard.", + "data_type": "string", + "required": false, + "primary": false, + "contains": [ + "cs workflow definition name" + ], + "value_list": [], + "default": "", + "order": 0, + "name": "name", + "id": 1, + "param_name": "name" + }, + "definition_id": { + "description": "Builds a filter to search for a definition matching the provided value. '*' may used as a wildcard. If name is defined, this parameter is ignored", + "data_type": "string", + "required": false, + "primary": false, + "contains": [ + "cs workflow definition id" + ], + "value_list": [], + "default": "", + "order": 1, + "name": "definition_id", + "id": 2, + "param_name": "definition_id" + }, + "fql_filter": { + "description": "FQL query specifying filter parameters. For further information reference FQL language. If name or definition is defined this parameter is ignored", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 2, + "name": "fql_filter", + "id": 3, + "param_name": "fql_filter" + }, + "offset": { + "description": "Starting pagination offset of records to return.", + "data_type": "numeric", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 3, + "name": "offset", + "id": 4, + "param_name": "offset" + }, + "limit": { + "description": "Maximum number of records to return.", + "data_type": "numeric", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 4, + "name": "limit", + "id": 5, + "param_name": "limit" + }, + "sort": { + "description": "Sort items by providing a comma separated list of property and direction (eg name.desc, time.asc). If direction is omitted, defaults to descending. For further information reference FQL language.", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 5, + "name": "sort", + "id": 6, + "param_name": "sort" + } + }, + "output": [ + { + "data_path": "action_result.parameter.name", + "data_type": "string", + "contains": [ + "cs workflow definition name" + ] }, + { + "data_path": "action_result.parameter.definition_id", + "data_type": "string", + "contains": [ + "cs workflow definition id" + ] + }, + { + "data_path": "action_result.parameter.fql_filter", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.offset", + "data_type": "numeric" + }, + { + "data_path": "action_result.parameter.limit", + "data_type": "numeric" + }, + { + "data_path": "action_result.parameter.sort", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.resources.*.id", + "data_type": "string", + "contains": [ + "cs workflow definition id" + ], + "column_name": "ID", + "column_order": 0 + }, + { + "data_path": "action_result.data.*.resources.*.name", + "data_type": "string", + "column_name": "Name", + "contains": [ + "cs workflow definition name" + ], + "column_order": 1 + }, + { + "data_path": "action_result.data.*.resources.*.enabled", + "data_type": "boolean", + "column_name": "Enabled", + "column_order": 2 + }, + { + "data_path": "action_result.data.*.resources.*.version", + "data_type": "numeric", + "column_name": "Version", + "column_order": 3 + }, + { + "data_path": "action_result.data.*.resources.*.description", + "data_type": "string", + "column_name": "Description", + "column_order": 4 + }, + { + "data_path": "action_result.data.*.resources.*.last_modified_timestamp", + "data_type": "string", + "column_name": "Last Modified Timestamp", + "column_order": 5 + }, + { + "data_path": "action_result.status", + "data_type": "string" + }, + { + "data_path": "action_result.message", + "data_type": "string" + }, + { + "data_path": "action_result.summary.count", + "data_type": "numeric" + }, + { + "data_path": "action_result.summary.trace_id", + "data_type": "string" + }, + { + "data_path": "action_result.status", + "data_type": "string" + }, + { + "data_path": "action_result.message", + "data_type": "string" + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric" + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric" + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "execute workflow", + "identifier": "execute_workflow", + "description": "Execute CS workflow", + "verbose": "Execute an on-demand workflow. Response will contain the execution ID.", + "type": "generic", + "read_only": false, + "parameters": { + "definition_id": { + "description": "One or more comma separated definition ID to execute; either a name or an ID can be specified. Only one of definition_id or name must be specified", + "data_type": "string", + "required": false, + "primary": true, + "contains": [ + "cs workflow definition id" + ], + "value_list": [], + "default": "", + "order": 0, + "name": "definition_id", + "id": 1, + "param_name": "definition_id" + }, + "name": { + "description": "Workflow name to execute; either a name or an ID can be specified. Only one of definition_id or name must be specified", + "data_type": "string", + "required": false, + "primary": true, + "contains": [ + "cs workflow definition name" + ], + "value_list": [], + "default": "", + "order": 1, + "name": "name", + "id": 2, + "param_name": "name" + }, + "key": { + "description": "Key used to help deduplicate executions, if unset a new UUID is used", + "data_type": "string", + "required": false, + "primary": true, + "contains": [ + "cs workflow execution key" + ], + "value_list": [], + "default": "", + "order": 2, + "name": "key", + "id": 3, + "param_name": "key" + }, + "depth": { + "description": "Used to record the execution depth to help limit execution loops when a workflow triggers another. The maximum depth is 4.", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 3, + "name": "depth", + "id": 4, + "param_name": "depth" + }, + "source_event_url": { + "description": "Used to record a URL to the source that led to triggering this workflow", + "data_type": "string", + "required": false, + "primary": false, + "contains": [ + "url" + ], + "value_list": [], + "default": "", + "order": 4, + "name": "source_event_url", + "id": 5, + "param_name": "source_event_url" + }, + "body": { + "description": "Full body payload in JSON format.", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 5, + "name": "body", + "id": 6, + "param_name": "body" + }, + "body_key1": { + "description": "key1 to be merged into body", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 6, + "name": "body_key1", + "id": 7, + "param_name": "body_key1" + }, + "body_value1": { + "description": "value for key1", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 7, + "name": "body_value1", + "id": 8, + "param_name": "body_value1" + }, + "body_key2": { + "description": "key2 to be merged into body", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 8, + "name": "body_key2", + "id": 9, + "param_name": "body_key2" + }, + "body_value2": { + "description": "value for key2", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 9, + "name": "body_value2", + "id": 10, + "param_name": "body_value2" + }, + "body_key3": { + "description": "key3 to be merged into body", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 10, + "name": "body_key3", + "id": 11, + "param_name": "body_key3" + }, + "body_value3": { + "description": "value for key3", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 11, + "name": "body_value3", + "id": 12, + "param_name": "body_value3" + }, + "body_key4": { + "description": "key4 to be merged into body", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 12, + "name": "body_key4", + "id": 13, + "param_name": "body_key4" + }, + "body_value4": { + "description": "value for key4", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 13, + "name": "body_value4", + "id": 14, + "param_name": "body_value4" + }, + "body_key5": { + "description": "key5 to be merged into body", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 14, + "name": "body_key5", + "id": 15, + "param_name": "body_key5" + }, + "body_value5": { + "description": "value for key5", + "data_type": "string", + "required": false, + "primary": false, + "value_list": [], + "default": "", + "order": 15, + "name": "body_value5", + "id": 16, + "param_name": "body_value5" + } + }, + "output": [ + { + "data_path": "action_result.parameter.definition_id", + "data_type": "string", + "contains": [ + "cs workflow definition id" + ] + }, + { + "data_path": "action_result.parameter.name", + "data_type": "string", + "contains": [ + "cs workflow definition name" + ] + }, + { + "data_path": "action_result.parameter.key", + "data_type": "string", + "contains": [ + "cs workflow execution key" + ] + }, + { + "data_path": "action_result.parameter.depth", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.source_event_url", + "data_type": "string", + "contains": [ + "url" + ] + }, + { + "data_path": "action_result.parameter.body", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_key1", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_value1", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_key2", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_value2", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_key3", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_value3", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_key4", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_value4", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_key5", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.body_value5", + "data_type": "string" + }, + { + "data_path": "action_result.meta.trace_id", + "data_type": "string" + }, + { + "data_path": "action_result.meta.pagination.limit", + "data_type": "numeric" + }, + { + "data_path": "action_result.meta.pagination.total", + "data_type": "numeric" + }, + { + "data_path": "action_result.meta.pagination.offset", + "data_type": "numeric" + }, + { + "data_path": "action_result.meta.powered_by", + "data_type": "string" + }, + { + "data_path": "action_result.meta.query_time", + "data_type": "numeric" + }, + { + "data_path": "action_result.data.*.resources", + "data_type": "string", + "contains": [ + "cs workflow execution id" + ], + "column_name": "Execution ID", + "column_order": 0 + }, + { + "data_path": "action_result.summary.ids", + "data_type": "string", + "contains": [ + "cs workflow execution id" + ] + }, + { + "data_path": "action_result.summary.count", + "data_type": "numeric" + }, + { + "data_path": "action_result.summary.trace_id", + "data_type": "string" + }, + { + "data_path": "action_result.status", + "data_type": "string" + }, + { + "data_path": "action_result.message", + "data_type": "string" + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric" + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric" + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "get workflow results", + "identifier": "get_workflow_results", + "description": "Get result(s) of one or more workflow executions", + "verbose": "Get the results of one or more workflow executions. Successful requests return an HTTP 200 code and an array with detailed results.", + "type": "investigate", + "read_only": true, + "parameters": { + "ids": { + "description": "One or more comma/space separated execution IDs", + "data_type": "string", + "required": false, + "primary": false, + "contains": [ + "cs workflow execution id" + ], + "value_list": [], + "default": "", + "order": 0, + "name": "ids" + } + }, + "output": [ + { + "data_path": "action_result.parameter.ids", + "data_type": "string", + "contains": [ + "cs workflow execution id" + ] + }, + { + "data_path": "action_result.data.*.meta.trace_id", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.meta.pagination.limit", + "data_type": "numeric" + }, + { + "data_path": "action_result.data.*.meta.pagination.total", + "data_type": "numeric" + }, + { + "data_path": "action_result.data.*.meta.pagination.offset", + "data_type": "numeric" + }, + { + "data_path": "action_result.data.*.meta.powered_by", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.meta.query_time", + "data_type": "numeric" + }, + { + "data_path": "action_result.data.*.resources.*.status", + "data_type": "string", + "column_name": "Status", + "column_order": 0 + }, + { + "data_path": "action_result.data.*.resources.*.loops", + "data_type": "string", + "column_name": "Loops", + "column_order": 1 + }, + { + "data_path": "action_result.data.*.resources.*.retryable", + "data_type": "boolean", + "column_name": "Retryable", + "column_order": 2 + }, + { + "data_path": "action_result.data.*.resources.*.execution_id", + "data_type": "string", + "column_name": "Execution ID", + "column_order": 3, + "contains": [ + "cs workflow execution id" + ] + }, + { + "data_path": "action_result.data.*.resources.*.definition_id", + "data_type": "string", + "column_name": "Definition ID", + "column_order": 4, + "contains": [ + "cs workflow definition id" + ] + }, + { + "data_path": "action_result.data.*.resources.*.definition_version", + "data_type": "numeric", + "column_name": "Definition Version", + "column_order": 5 + }, + { + "data_path": "action_result.data.*.resources.*.start_timestamp", + "data_type": "string", + "column_name": "Start Timestamp", + "column_order": 6 + }, + { + "data_path": "action_result.data.*.resources.*.end_timestamp", + "data_type": "string", + "column_name": "End Timestamp", + "column_order": 7 + }, + { + "data_path": "action_result.data.*.resources.*.ancestor_executions", + "data_type": "string", + "column_name": "Ancestor Executions", + "column_order": 8 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.name", + "data_type": "string", + "column_name": "Trigger Name", + "column_order": 9 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.CID", + "data_type": "string", + "column_name": "Trigger Result CID", + "column_order": 10 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.Name", + "data_type": "string", + "column_name": "Trigger Result Name", + "column_order": 11 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.Category", + "data_type": "string", + "column_name": "Trigger Result Category", + "column_order": 12 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.TriggerIP", + "data_type": "string", + "column_name": "Trigger Result IP", + "column_order": 13 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.somefield", + "data_type": "string", + "column_name": "Trigger Result Somefield", + "column_order": 14 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.ObservedTime", + "data_type": "string", + "column_name": "Trigger Result Observed Time", + "column_order": 15 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.SourceEventURL", + "data_type": "string", + "column_name": "Trigger Result Source Event URL", + "column_order": 16 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.ExecutionDepth", + "data_type": "numeric", + "column_name": "Trigger Result Execution Depth", + "column_order": 17 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.TriggerSource.User.UUID", + "data_type": "string", + "column_name": "Trigger Result Source User UUID", + "column_order": 18 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.TriggerSource.User.Email", + "data_type": "string", + "column_name": "Trigger Result Source User Email", + "column_order": 19 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.TriggerSource.Workflow.ExecutionID", + "data_type": "string", + "column_name": "Trigger Result Source Workflow Execution ID", + "column_order": 20 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.TriggerSource.Workflow.DefinitionID", + "data_type": "string", + "column_name": "Trigger Result Source Workflow Definition ID", + "column_order": 21 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.TriggerSource.Workflow.DefinitionVersion", + "data_type": "numeric", + "column_name": "Trigger Result Source Workflow Definition Version", + "column_order": 22 + }, + { + "data_path": "action_result.data.*.resources.*.trigger.result.TriggerSource.APIClient.ID", + "data_type": "string", + "column_name": "Trigger Result Source API Client ID", + "column_order": 23 + }, + { + "data_path": "action_result.data.*.resources.*.activities.*.id", + "data_type": "string", + "column_name": "Activity ID", + "column_order": 24 + }, + { + "data_path": "action_result.data.*.resources.*.activities.*.name", + "data_type": "string", + "column_name": "Activity Name", + "column_order": 25 + }, + { + "data_path": "action_result.data.*.resources.*.activities.*.type", + "data_type": "string", + "column_name": "Activity Type", + "column_order": 26 + }, + { + "data_path": "action_result.data.*.resources.*.activities.*.status", + "data_type": "string", + "column_name": "Activity Status", + "column_order": 27 + }, + { + "data_path": "action_result.data.*.resources.*.activities.*.node_id", + "data_type": "string", + "column_name": "Activity Node ID", + "column_order": 28 + }, + { + "data_path": "action_result.data.*.resources.*.activities.*.start_timestamp", + "data_type": "string", + "column_name": "Activity Start Timestamp", + "column_order": 29 + }, + { + "data_path": "action_result.data.*.resources.*.activities.*.end_timestamp", + "data_type": "string", + "column_name": "Activity End Timestamp", + "column_order": 30 + }, + { + "data_path": "action_result.summary.trace_id", + "data_type": "string" + }, + { + "data_path": "action_result.summary.count", + "data_type": "numeric" + }, + { + "data_path": "action_result.status", + "data_type": "string" + }, + { + "data_path": "action_result.message", + "data_type": "string" + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric" + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric" + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" } ], "pip39_dependencies": { diff --git a/crowdstrikeoauthapi_connector.py b/crowdstrikeoauthapi_connector.py index 7d37f6c..27505f8 100644 --- a/crowdstrikeoauthapi_connector.py +++ b/crowdstrikeoauthapi_connector.py @@ -4469,6 +4469,195 @@ def _get_resource_report(self, action_result, param, resource_id): ) return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_get_workflow_definitions(self, param): + self.save_progress("In action handler for: {0}".format(self.get_action_identifier())) + # Add an action result object to self (BaseConnector) to represent the action for this param + action_result = self.add_action_result(ActionResult(dict(param))) + + from urllib.parse import quote as urlquote + name = param.get('name', '') + definition_id = param.get('definition_id', '') + fql_filter = urlquote(param.get('fql_filter', '')) + offset = param.get('offset', '') + limit = param.get('limit', '') + sort = urlquote(param.get('sort', '')) + + if name: + name = f"name:'{name}'" + definition_id = None + fql_filter = None + + elif definition_id: + definition_id = f"id:'{definition_id}'" + fql_filter = None + + endpoint = [CROWDSTRIKE_GET_WORKFLOW_DEFINITIONS_ENDPOINT + '?'] + endpoint += [f"name={name}"] if name else [] + endpoint += [f"definition_id={definition_id}"] if definition_id else [] + endpoint += [f"fql_filter={fql_filter}"] if fql_filter else [] + endpoint += [f"offset={offset}"] if offset else [] + endpoint += [f"limit={limit}"] if limit else [] + endpoint += [f"sort={sort}"] if sort else [] + endpoint = [x.strip() for x in endpoint if x.strip()] + endpoint = '&'.join(endpoint).replace('&&', '&').replace('?&', '?').strip('?').strip('&').strip('?') + self.save_progress(f"endpoint: {endpoint}") + + ret_val, response = self._make_rest_call_helper_oauth2(action_result, endpoint, method="get") + status_code = self._rest_response.status_code + try: + response_data = self._rest_response.json() + except: + response_data = self._rest_response.text + + summary = {} + self.save_progress(f"status code: {status_code}") + if isinstance(response_data, dict): + self.save_progress(json.dumps(response_data, indent=4)) + action_result.add_data(response_data) + trace_id = response_data.get('meta', {}).get('trace_id', 'n/a') + if trace_id: + summary['trace_id'] = trace_id + definitions = response_data.get('resources', []) + summary['count']= len(definitions) + summary.update({d['name']:d['id'] for d in definitions}) + action_result.set_summary(summary) + + else: + self.save_progress(f"<{self._rest_response.text}>") + action_result.add_data(response_data) + + if phantom.is_fail(ret_val) or (status_code < 200 or status_code > 299): + return action_result #.set_status(phantom.APP_ERROR) + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_execute_workflow(self, param): + self.save_progress("In action handler for: {0}".format(self.get_action_identifier())) + # Add an action result object to self (BaseConnector) to represent the action for this param + action_result = self.add_action_result(ActionResult(dict(param))) + + definition_id = param.get('definition_id', '') + name = param.get('name', '') + key = param.get('key', '') + depth = param.get('depth', '') + source_event_url = param.get('source_event_url', '') + body = param.get('body', '') + + if name and definition_id: + self.save_progress(f"Warning: both definition_id ({definition_id}) and name ({name}) defined; using definition_id instead of name") + + if not name and not definition_id: + error_message = "Error: no definition id or name specified" + self.save_progress(error_message) + return action_result.set_status(phantom.APP_ERROR,error_message) + + if definition_id: + definition_id = [f"definition_id={x.strip()}" for x in definition_id.split(',') if x.strip()] + if not definition_id: + error_message = "Error: definition id fails parameter validation" + self.save_progress(error_message) + return action_result.set_status(phantom.APP_ERROR,error_message) + + try: + if body: + body = json.loads(body) + else: + body = {} + except: + body = {} + self.save_progress(f"Warning: body is not json, reseting to empty dictionary") + + # [1..5] inclusive + for index in range(1,6): + key = param.get(f"body_key{index}") + value = param.get(f"body_value{index}", '') + if key: + body[key] = value + + endpoint = [CROWDSTRIKE_EXECUTE_WORKFLOW_ENDPOINT + '?'] + endpoint += definition_id if definition_id else [] + endpoint += [f"name={name}"] if name and not definition_id else [] + endpoint += [f"key={key}"] if key else [] + endpoint += [f"depth={depth}"] if depth else [] + endpoint += [f"source_event_url={source_event_url}"] if source_event_url else [] + endpoint = list(filter(lambda x: x.strip(), endpoint)) + endpoint = '&'.join(endpoint).replace('&&', '&').replace('?&', '?').strip('?').strip('&').strip('?') + self.save_progress(f"endpoint: {endpoint}") + self.save_progress(json.dumps(body, indent=4, default=str)) + + ret_val, response = self._make_rest_call_helper_oauth2(action_result, endpoint, json_data=body, method="post") + status_code = self._rest_response.status_code + try: + response_data = self._rest_response.json() + except: + response_data = self._rest_response.text + + summary = {} + self.save_progress(f"status code: {status_code}") + if isinstance(response_data, dict): + self.save_progress(json.dumps(response_data, indent=4)) + action_result.add_data(response_data) + trace_id = response_data.get('meta', {}).get('trace_id', 'n/a') + if trace_id: + summary['trace_id'] = trace_id + execute_ids = response_data.get('resources', []) + summary['count']= len(execute_ids) + summary['ids'] = execute_ids + action_result.set_summary(summary) + else: + self.save_progress(f"<{self._rest_response.text}>") + action_result.add_data(response_data) + + if phantom.is_fail(ret_val) or (status_code < 200 or status_code > 299): + return action_result #.set_status(phantom.APP_ERROR) + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_get_workflow_results(self, param): + self.save_progress("In action handler for: {0}".format(self.get_action_identifier())) + # Add an action result object to self (BaseConnector) to represent the action for this param + action_result = self.add_action_result(ActionResult(dict(param))) + + execution_ids = param.get('ids', '') + + if execution_ids: + execution_ids = [f"ids={x.strip()}" for x in execution_ids.split(',') if x.strip()] + if not execution_ids: + error_message = "Error: eexecution ids fails parameter validation" + self.save_progress(error_message) + return action_result.set_status(phantom.APP_ERROR,error_message) + + endpoint = [CROWDSTRIKE_GET_WORKFLOW_RESULTS_ENDPOINT + '?'] + endpoint += execution_ids if execution_ids else [] + endpoint = [x.strip() for x in endpoint if x.strip()] + endpoint = '&'.join(endpoint).replace('&&', '&').replace('?&', '?').strip('?').strip('&').strip('?') + self.save_progress(f"endpoint: {endpoint}") + + ret_val, response = self._make_rest_call_helper_oauth2(action_result, endpoint, method="get") + status_code = self._rest_response.status_code + try: + response_data = self._rest_response.json() + except: + response_data = self._rest_response.text + + summary = {} + self.save_progress(f"status code: {status_code}") + if isinstance(response_data, dict): + self.save_progress(json.dumps(response_data, indent=4)) + action_result.add_data(response_data) + trace_id = response_data.get('meta', {}).get('trace_id', 'n/a') + if trace_id: + summary['trace_id'] = trace_id + results = response_data.get('resources', []) + summary['count']= len(results) + summary.update({r['execution_id']:r['status'] for r in results}) + action_result.set_summary(summary) + else: + self.save_progress(f"<{self._rest_response.text}>") + action_result.add_data(response_data) + + if phantom.is_fail(ret_val) or (status_code < 200 or status_code > 299): + return action_result #.set_status(phantom.APP_ERROR) + return action_result.set_status(phantom.APP_SUCCESS) def _process_empty_response(self, response, action_result): """This function is used to process empty response. @@ -5098,6 +5287,9 @@ def handle_action(self, param): "create_ioa_rule": self._handle_create_ioa_rule, "update_ioa_rule": self._handle_update_ioa_rule, "delete_ioa_rule": self._handle_delete_ioa_rule, + "get_workflow_definitions": self._handle_get_workflow_definitions, + "execute_workflow": self._handle_execute_workflow, + "get_workflow_results": self._handle_get_workflow_results, } action = self.get_action_identifier() diff --git a/crowdstrikeoauthapi_consts.py b/crowdstrikeoauthapi_consts.py index cc9f8c5..38dad2d 100644 --- a/crowdstrikeoauthapi_consts.py +++ b/crowdstrikeoauthapi_consts.py @@ -261,3 +261,8 @@ CROWDSTRIKE_UPDATE_ALERT_ENDPOINT = "/alerts/entities/alerts/v3" CROWDSTRIKE_GET_ALERT_DETAILS_ENDPOINT = "/alerts/entities/alerts/v2" + +# Crowdstrike workflow api +CROWDSTRIKE_GET_WORKFLOW_DEFINITIONS_ENDPOINT = "/workflows/combined/definitions/v1" +CROWDSTRIKE_EXECUTE_WORKFLOW_ENDPOINT = "/workflows/entities/execute/v1" +CROWDSTRIKE_GET_WORKFLOW_RESULTS_ENDPOINT = "/workflows/entities/execution-results/v1"