diff --git a/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py b/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py index 9cb2c7ceff0..f70bcd612b1 100644 --- a/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py +++ b/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py @@ -1572,6 +1572,52 @@ def remove_user_role(self, user_emails: list[str]) -> dict[str, dict[str, str]]: }}, ) + def terminate_on_agent(self, + url_suffix_endpoint: str, + id_key: str, + id_value: str, + agent_id: str, + process_name: Optional[str], + incident_id: Optional[str]) -> dict[str, dict[str, str]]: + """ + Terminate a specific process or a the causality on an agent. + + :type url_suffix_endpoint: ``str`` + :param agent_id: The endpoint of the command(terminate_causality or terminate_process). + + :type agent_id: ``str`` + :param agent_id: The ID of the agent. + + :type id_key: ``str`` + :param id_key: The key name ID- causality_id or process_id. + + :type id_key: ``str`` + :param id_key: The ID data- causality_id or process_id. + + :type process_name: ``Optional[str]`` + :param process_name: The name of the process. Optional. + + :type incident_id: ``Optional[str]`` + :param incident_id: The ID of the incident. Optional. + + :return: The response from the API. + :rtype: ``dict[str, dict[str, str]]`` + """ + request_data: Dict[str, Any] = { + "agent_id": agent_id, + id_key: id_value, + } + if process_name: + request_data["process_name"] = process_name + if incident_id: + request_data["incident_id"] = incident_id + response = self._http_request( + method='POST', + url_suffix=f'/endpoints/{url_suffix_endpoint}/', + json_data={"request_data": request_data}, + ) + return response.get('reply') + class AlertFilterArg: def __init__(self, search_field: str, search_type: Optional[str], arg_type: str, option_mapper: dict = None): @@ -1651,7 +1697,8 @@ def run_polling_command(client: CoreClient, results_function: Callable, polling_field: str, polling_value: List, - stop_polling: bool = False) -> CommandResults: + stop_polling: bool = False, + values_raise_error: List = []) -> CommandResults: """ Arguments: args: args @@ -1664,6 +1711,7 @@ def run_polling_command(client: CoreClient, polling_value: list of values of the polling_field we want to check. The list can contain values to stop or continue polling on, not both. stop_polling: True - polling_value stops the polling. False - polling_value does not stop the polling. + values_raise_error: list of polling values that require raising an error. Return: command_results(CommandResults) @@ -1708,6 +1756,8 @@ def run_polling_command(client: CoreClient, result = outputs_result_func.get(polling_field) if isinstance(outputs_result_func, dict) else \ outputs_result_func[0].get(polling_field) cond = result not in polling_value if stop_polling else result in polling_value + if values_raise_error and result in values_raise_error: + raise DemistoException(f"The command {cmd} failed. Received status {result}") if cond: # schedule next poll polling_args = { @@ -1906,7 +1956,7 @@ def endpoint_scan_command(client: CoreClient, args) -> CommandResults: def action_status_get_command(client: CoreClient, args) -> CommandResults: action_id_list = argToList(args.get('action_id', '')) action_id_list = [arg_to_int(arg=item, arg_name=str(item)) for item in action_id_list] - + demisto.debug(f'action_status_get_command {action_id_list=}') result = [] for action_id in action_id_list: data = client.action_status_get(action_id) @@ -1915,7 +1965,7 @@ def action_status_get_command(client: CoreClient, args) -> CommandResults: result.append({ 'action_id': action_id, 'endpoint_id': endpoint_id, - 'status': status + 'status': status, }) return CommandResults( @@ -4403,3 +4453,84 @@ def get_incidents_command(client, args): }, raw_incidents ) + + +def terminate_process_command(client, args) -> CommandResults: + """ + AVAILABLE ONLY TO XDR3.12 / XSIAM2.4 + Terminate the process command for a specific agent and instance IDs. + + :type client: ``Client`` + :param client: The client to use for making API calls. + + :type args: ``Dict[str, Any]`` + :param args: The arguments for the command. + + :return: The results of the command. + :rtype: ``CommandResults`` + """ + agent_id = args.get('agent_id') + instance_ids = argToList(args.get('instance_id')) + process_name = args.get('process_name') + incident_id = args.get('incident_id') + replies: List[Dict[str, Any]] = [] + for instance_id in instance_ids: + reply_per_instance_id = client.terminate_on_agent( + url_suffix_endpoint='terminate_process', + id_key='instance_id', + id_value=instance_id, + agent_id=agent_id, + process_name=process_name, + incident_id=incident_id + ) + action_id = reply_per_instance_id.get("group_action_id") + demisto.debug(f'Action terminate process succeeded with action_id={action_id}') + replies.append({"action_id": action_id}) + + return CommandResults( + readable_output=tableToMarkdown(f'Action terminate process created on instance ids: {", ".join(instance_ids)}', replies), + outputs={ + f'{args.get("integration_context_brand", "CoreApiModule")}' + f'.TerminateProcess(val.actionId && val.actionId == obj.actionId)': replies}, + raw_response=replies + ) + + +def terminate_causality_command(client, args) -> CommandResults: + """ + AVAILABLE ONLY TO XDR3.12 / XSIAM2.4 + Terminate the causality command for a specific agent and causality IDs. + + :type client: ``Client`` + :param client: The client to use for making API calls. + + :type args: ``Dict[str, Any]`` + :param args: The arguments for the command. + + :return: The results of the command. + :rtype: ``CommandResults`` + """ + agent_id = args.get('agent_id') + causality_ids = argToList(args.get('causality_id')) + process_name = args.get('process_name') + incident_id = args.get('incident_id') + replies: List[Dict[str, Any]] = [] + for causality_id in causality_ids: + reply_per_instance_id = client.terminate_on_agent( + url_suffix_endpoint='terminate_causality', + id_key='causality_id', + id_value=causality_id, + agent_id=agent_id, + process_name=process_name, + incident_id=incident_id + ) + action_id = reply_per_instance_id.get("group_action_id") + demisto.debug(f'Action terminate process succeeded with action_id={action_id}') + replies.append({"action_id": action_id}) + + return CommandResults( + readable_output=tableToMarkdown(f'Action terminate causality created on {",".join(causality_ids)}', replies), + outputs={f'{args.get("integration_context_brand", "CoreApiModule")}.TerminateProcess(val.actionId == obj.actionId)': + replies}, + raw_response=replies + ) diff --git a/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule_test.py b/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule_test.py index 420d6193cbb..99c69c2b93b 100644 --- a/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule_test.py +++ b/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule_test.py @@ -4228,3 +4228,116 @@ def test_request_for_bin_file_via_demisto_call(mocker, allow_bin_response): assert res == test_bin_data except DemistoException as e: assert f'{ALLOW_BIN_CONTENT_RESPONSE_SERVER_VERSION}-{ALLOW_BIN_CONTENT_RESPONSE_BUILD_NUM}' in str(e) + + +def test_terminate_process_command(mocker): + """ + Given: + - An XSIAM machine with a build version that supports demisto._apiCall() with RBAC validations. + - instance_id_1 + - instance_id_2 + - agent_id + When: + - Calling the terminate_process_command method. + Then: + - case 1 - Make sure the response are as expected (action_id). + """ + from CoreIRApiModule import CoreClient, terminate_process_command + client = CoreClient( + base_url=f'{Core_URL}/public_api/v1', headers={}, + ) + + mocker.patch("CoreIRApiModule.FORWARD_USER_RUN_RBAC", new=True) + mocker.patch.object(demisto, "_apiCall", side_effect=[ + {'name': '/api/webapp/public_api/v1/endpoints/terminate_process', + 'status': 200, + 'data': json.dumps({'reply': {'group_action_id': 1}})}, + {'name': '/api/webapp/public_api/v1/endpoints/terminate_process', + 'status': 200, + 'data': json.dumps({'reply': {'group_action_id': 2}})} + ] + ) + + result = terminate_process_command(client=client, args={'agent_id': '1', 'instance_id': ['instance_id_1', 'instance_id_2']}) + assert result.readable_output == ('### Action terminate process created on instance ids:' + ' instance_id_1, instance_id_2\n|action_id|\n|---|\n| 1 |\n| 2 |\n') + assert result.raw_response == [{'action_id': 1}, {'action_id': 2}] + + +def test_terminate_causality_command(mocker): + """ + Given: + - An XSIAM machine with a build version that supports demisto._apiCall() with RBAC validations. + - causality_id + - agent_id + When: + - Calling the terminate_causality_command method. + Then: + - case 1 - Make sure the response are as expected (action_id). + """ + from CoreIRApiModule import CoreClient, terminate_causality_command + client = CoreClient( + base_url=f'{Core_URL}/public_api/v1', headers={}, + ) + + mocker.patch("CoreIRApiModule.FORWARD_USER_RUN_RBAC", new=True) + mocker.patch.object(demisto, "_apiCall", side_effect=[ + {'name': '/api/webapp/public_api/v1/endpoints/terminate_causality', + 'status': 200, + 'data': json.dumps({'reply': {'group_action_id': 1}})}, + {'name': '/api/webapp/public_api/v1/endpoints/terminate_causality', + 'status': 200, + 'data': json.dumps({'reply': {'group_action_id': 2}})} + ] + ) + + result = terminate_causality_command(client=client, args={'agent_id': '1', 'causality_id': [ + 'causality_id_1', 'causality_id_2']}) + assert result.readable_output == ('### Action terminate causality created on causality_id_1,' + 'causality_id_2\n|action_id|\n|---|\n| 1 |\n| 2 |\n') + assert result.raw_response == [{'action_id': 1}, {'action_id': 2}] + + +def test_run_polling_command_values_raise_error(mocker): + """ + Given - + - run_polling_command arguments. + - + + When - + - Running the run_polling_command + + Then + - Make sure that an error is raised with the correct output. + """ + from CoreIRApiModule import run_polling_command + from CommonServerPython import DemistoException, ScheduledCommand + from unittest.mock import Mock + + polling_args = { + 'endpoint_ids': '1', 'command_decision_field': 'action_id', 'action_id': '1', 'hide_polling_output': True + } + mocker.patch.object(ScheduledCommand, 'raise_error_if_not_supported', return_value=None) + client = Mock() + mock_command_results = Mock() + mock_command_results.raw_response = {"status": "TIMEOUT"} + mock_command_results.return_value = mock_command_results + client.get_command_results.return_value = mock_command_results + + with pytest.raises(DemistoException) as e: + run_polling_command(client=client, + args=polling_args, + cmd="core-terminate-causality", + command_function=Mock(), + command_decision_field="action_id", + results_function=mock_command_results, + polling_field="status", + polling_value=["PENDING", + "IN_PROGRESS", + "PENDING_ABORT"], + values_raise_error=["FAILED", + "TIMEOUT", + "ABORTED", + "CANCELED"] + ) + assert str(e.value) == 'The command core-terminate-causality failed. Received status TIMEOUT' diff --git a/Packs/Core/Integrations/CortexCoreIR/CortexCoreIR.py b/Packs/Core/Integrations/CortexCoreIR/CortexCoreIR.py index d91dba54317..f6ab42c04ef 100644 --- a/Packs/Core/Integrations/CortexCoreIR/CortexCoreIR.py +++ b/Packs/Core/Integrations/CortexCoreIR/CortexCoreIR.py @@ -26,6 +26,9 @@ 'core-get-cmd-analytics-prevalence': 'cmd', } +TERMINATE_BUILD_NUM = '1398786' +TERMINATE_SERVER_VERSION = '8.9.0' + class Client(CoreClient): @@ -471,6 +474,44 @@ def main(): # pragma: no cover elif command == 'core-get-incidents': return_outputs(*get_incidents_command(client, args)) + elif command == 'core-terminate-process': + if not is_demisto_version_ge(version=TERMINATE_SERVER_VERSION, + build_number=TERMINATE_BUILD_NUM): + raise DemistoException('This command is only available for XSIAM 2.4') + return_results(run_polling_command(client=client, + args=args, + cmd="core-terminate-process", + command_function=terminate_process_command, + command_decision_field="action_id", + results_function=action_status_get_command, + polling_field="status", + polling_value=["PENDING", + "IN_PROGRESS", + "PENDING_ABORT" + ], + values_raise_error=["FAILED", + "TIMEOUT", + "ABORTED", + "CANCELED"])) + + elif command == 'core-terminate-causality': + if not is_demisto_version_ge(version=TERMINATE_BUILD_NUM, build_number=TERMINATE_BUILD_NUM): + raise DemistoException("This command is only available for XSIAM 2.4") + return_results(run_polling_command(client=client, + args=args, + cmd="core-terminate-causality", + command_function=terminate_causality_command, + command_decision_field="action_id", + results_function=action_status_get_command, + polling_field="status", + polling_value=["PENDING", + "IN_PROGRESS", + "PENDING_ABORT"], + values_raise_error=["FAILED", + "TIMEOUT", + "ABORTED", + "CANCELED"] + )) elif command in PREVALENCE_COMMANDS: return_results(handle_prevalence_command(client, command, args)) diff --git a/Packs/Core/Integrations/CortexCoreIR/CortexCoreIR.yml b/Packs/Core/Integrations/CortexCoreIR/CortexCoreIR.yml index 3375c6fcc12..bf7aa45d866 100644 --- a/Packs/Core/Integrations/CortexCoreIR/CortexCoreIR.yml +++ b/Packs/Core/Integrations/CortexCoreIR/CortexCoreIR.yml @@ -3016,6 +3016,70 @@ script: - contextPath: Core.ScriptResult.results.retention_date description: Timestamp in which the retrieved files will be deleted from the server. type: Date + - arguments: + - description: The agent ID. + name: agent_id + isArray: false + required: true + - description: The instance ID. + name: instance_id + isArray: true + required: true + - description: The process name. + name: process_name + isArray: false + required: false + - description: The incident ID. + name: incident_id + isArray: false + required: false + - description: The action ID. For polling use. + isArray: true + name: action_id + deprecated: true + - description: Interval in seconds between each poll. + name: interval_in_seconds + - description: Polling timeout in seconds. + name: timeout_in_seconds + name: core-terminate-process + description: Terminate a process by its instance ID. Available only for XSIAM 2.4. + polling: true + outputs: + - contextPath: Core.TerminateProcess.action_id + description: The action ID. + type: String + - arguments: + - description: The agent ID. + name: agent_id + isArray: false + required: true + - description: The causality ID. + name: causality_id + isArray: true + required: true + - description: The process name. + name: process_name + isArray: false + required: false + - description: The incident ID. + name: incident_id + isArray: false + required: false + - description: The action ID. For polling use. + isArray: true + name: action_id + deprecated: true + - description: Interval in seconds between each poll. + name: interval_in_seconds + - description: Polling timeout in seconds. + name: timeout_in_seconds + name: core-terminate-causality + description: Terminate a process tree by its causality ID. Available only for XSIAM 2.4. + polling: true + outputs: + - contextPath: Core.TerminateCausality.action_id + description: The action id. + type: String runonce: false script: '-' subtype: python3 diff --git a/Packs/Core/Integrations/CortexCoreIR/README.md b/Packs/Core/Integrations/CortexCoreIR/README.md index 01532c7a77d..269ace7cdaa 100644 --- a/Packs/Core/Integrations/CortexCoreIR/README.md +++ b/Packs/Core/Integrations/CortexCoreIR/README.md @@ -2814,3 +2814,90 @@ Initiates a new endpoint script execution action using a script from the script >|---|---|---|---|---|---|---|---|---|---|---| >||222|111|1.1.1.1|test|STATUS_010_CONNECTED|COMPLETED_SUCCESSFULLY|0||0|| + +### core-terminate-process + +*** +Terminate a process by its instance ID. + +#### Base Command + +`core-terminate-process` + +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| agent_id | The agent ID. | Required | +| instance_id | The instance ID. | Required | +| process_name | The process name. | Optional | +| incident_id | The incident ID. | Optional | +| action_id | The action ID. For polling use. | Optional | +| interval_in_seconds | Interval in seconds between each poll. | Optional | +| timeout_in_seconds | Polling timeout in seconds. | Optional | + +#### Context Output + +| **Path** | **Type** | **Description** | +| --- | --- | --- | +| Core.TerminateProcess.action_id | String | The action ID. | +### core-terminate-causality + +*** +Stops a process by its causality ID. + +##### Command Example + +```!core-terminate-process agent_id=1 instance_id=1 process_name=process incident_id=2``` + +##### Context Example + +``` +{ + "Core.TerminateProcess": [ + { + "action_id": "1", + } + + ] +} +``` + +#### Base Command + +`core-terminate-causality` + +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| agent_id | The agent ID. | Required | +| causality_id | The causality ID. | Required | +| process_name | The process name. | Optional | +| incident_id | The incident ID. | Optional | +| action_id | The action ID. For polling use. | Optional | +| interval_in_seconds | Interval in seconds between each poll. | Optional | +| timeout_in_seconds | Polling timeout in seconds. | Optional | + +#### Context Output + +| **Path** | **Type** | **Description** | +| --- | --- | --- | +| Core.TerminateCausality.action_id | String | The action id. | + +##### Command Example + +```!core-terminate-causality agent_id=1 causality_id=1 process_name=process incident_id=2``` + +##### Context Example + +``` +{ + "Core.TerminateCausality": [ + { + "action_id": "1", + } + + ] +} +``` \ No newline at end of file diff --git a/Packs/Core/Integrations/CortexCoreIR/command_examples b/Packs/Core/Integrations/CortexCoreIR/command_examples index 4b63db77cd2..f255d69022b 100644 --- a/Packs/Core/Integrations/CortexCoreIR/command_examples +++ b/Packs/Core/Integrations/CortexCoreIR/command_examples @@ -37,4 +37,6 @@ !core-remove-blocklist-files hash_list=11d69fb388ff59e5ba6ca217ca04ecde6a38fa8fb306aa5f1b72e22bb7c3a252 !core-allowlist-files hash_list=11d69fb388ff59e5ba6ca217ca04ecde6a38fa8fb306aa5f1b72e22bb7c3a252 !core-remove-allowlist-files hash_list=11d69fb388ff59e5ba6ca217ca04ecde6a38fa8fb306aa5f1b72e22bb7c3a252 +!core-terminate-process agent_id=1 instance_id=1 incident_id=1 interval_in_seconds=1 timeout_in_seconds=1 +!core-terminate-causality agent_id=1 causality_id=1 incident_id=1 interval_in_seconds=1 timeout_in_seconds=1!core-script-run endpoint_ids=111 script_uid=111 polling_timeout_in_seconds=1200 timeout=1200 !core-script-run endpoint_ids=111 script_uid=111 polling_timeout_in_seconds=1200 timeout=1200 diff --git a/Packs/Core/ReleaseNotes/3_0_69.md b/Packs/Core/ReleaseNotes/3_0_69.md new file mode 100644 index 00000000000..69b32bdabe5 --- /dev/null +++ b/Packs/Core/ReleaseNotes/3_0_69.md @@ -0,0 +1,8 @@ + +#### Integrations + +##### Investigation & Response + +Added the following commands: + - ***core-terminate-process*** +- ***core-terminate-causality*** diff --git a/Packs/Core/pack_metadata.json b/Packs/Core/pack_metadata.json index e0ac64ee188..f4132e53057 100644 --- a/Packs/Core/pack_metadata.json +++ b/Packs/Core/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Core - Investigation and Response", "description": "Automates incident response", "support": "xsoar", - "currentVersion": "3.0.68", + "currentVersion": "3.0.69", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "", diff --git a/Packs/CortexXDR/ReleaseNotes/6_1_77.md b/Packs/CortexXDR/ReleaseNotes/6_1_77.md new file mode 100644 index 00000000000..6bc8d72260a --- /dev/null +++ b/Packs/CortexXDR/ReleaseNotes/6_1_77.md @@ -0,0 +1,14 @@ + +#### Integrations + +##### Palo Alto Networks Cortex XDR - Investigation and Response + +No changes related directly to this integration. + +##### Cortex XDR - IOC + +No changes related directly to this integration + +##### Cortex XDR - XQL Query Engine + +No changes related directly to this integration diff --git a/Packs/CortexXDR/pack_metadata.json b/Packs/CortexXDR/pack_metadata.json index 9ff2e90fcca..f9552eccdf6 100644 --- a/Packs/CortexXDR/pack_metadata.json +++ b/Packs/CortexXDR/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Cortex XDR by Palo Alto Networks", "description": "Automates Cortex XDR incident response, and includes custom Cortex XDR incident views and layouts to aid analyst investigations.", "support": "xsoar", - "currentVersion": "6.1.76", + "currentVersion": "6.1.77", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "", diff --git a/Packs/ctf01/ReleaseNotes/1_0_29.md b/Packs/ctf01/ReleaseNotes/1_0_29.md new file mode 100644 index 00000000000..12a2045027d --- /dev/null +++ b/Packs/ctf01/ReleaseNotes/1_0_29.md @@ -0,0 +1,7 @@ + diff --git a/Packs/ctf01/pack_metadata.json b/Packs/ctf01/pack_metadata.json index dcbb960dbc6..1c5f2638186 100644 --- a/Packs/ctf01/pack_metadata.json +++ b/Packs/ctf01/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Capture The Flag - 01", "description": "XSOAR's Capture the flag (CTF)", "support": "xsoar", - "currentVersion": "1.0.28", + "currentVersion": "1.0.29", "serverMinVersion": "8.2.0", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex",