KillProcessWrapper

A cross-vendor wrapper script that triggers a ‘process kill’ command - i.e executes the proper kill process command according to the vendor: CrowdstrikeFalcon or Cortex XDR. The script will only fail when the kill process action fails for both vendors.

python · Malware Investigation and Response

Details

IDKillProcessWrapper
Languagepython
From Version6.1.0
Docker Imagedemisto/python3:3.12.13.10404775

README

A cross-vendor wrapper script that triggers a process kill command - i.e executes the proper kill process command according to the vendor: CrowdstrikeFalcon or Cortex XDR.

The script will only fail when the kill process action fails for both vendors.

Script Data


Name Description
Script Type python3
Cortex XSOAR Version 6.1.0

Inputs


Argument Name Description
endpoint_id The Endpoint ID in which you would like to kill the given process.
process_id The ID of the process to kill. Either the process_id or the process_name must be specified.
process_name The name of the process to kill. Either the process_id or the process_name must be specified.
approve_action Are you sure you want to kill this process?

Outputs


Path Description Type
CrowdStrike.Command.kill The outputs of the CrowdStrike kill process command. List
CrowdStrike.Command.kill.Error The status of the CrowdStrike kill process command. String
CrowdStrike.Command.kill.HostID The endpoint ID of the process. String
CrowdStrike.Command.kill.ProcessID The ID of the process. String
PaloAltoNetworksXDR.ScriptRun The outputs of the Cortex XDR kill process command. List
PaloAltoNetworksXDR.ScriptRun.action_id The ID of the kill process action initiated. Number
PaloAltoNetworksXDR.ScriptRun.endpoints_count The number of endpoints the action was initiated on. Number
PaloAltoNetworksXDR.ScriptRun.status The status of the kill process action. Number
"""
Unit Tests for KillProcessWrapper script.
"""

import demistomock as demisto
import pytest


@pytest.mark.parametrize(
    "endpoint_id, process_id, process_name, expected_commands",
    [
        ("endpoint_id_test", "process_id_test", None, ["cs-falcon-rtr-kill-process"]),
        ("endpoint_id_test", None, "process_name_test", ["xdr-run-script-kill-process"]),
        (
            "endpoint_id_test",
            "process_id_test",
            "process_name_test",
            ["xdr-run-script-kill-process", "cs-falcon-rtr-kill-process"],
        ),
    ],
)
def test_create_commands(endpoint_id, process_id, process_name, expected_commands):
    """
    Given:
        1. An endpoint ID and a process ID (without a process name).

        2. An endpoint ID and a process name (without a process ID).

        3. An endpoint ID and a process ID and a process name.

    When:
        Running the 'create_commands' function.

    Then:
        1. Verify that the returned command name is the CrowdstrikeFalcon kill process command.

        2. Verify that the returned command name is the Cortex XDR kill process command.

        3. Verify that the returned command names are both CrowdstrikeFalcon and Cortex XDR kill process commands.
    """

    from KillProcessWrapper import create_commands

    commands = create_commands(endpoint_id, process_id, process_name)
    assert commands[0].commands[0] == expected_commands[0]
    if len(expected_commands) == 2:
        assert commands[1].commands[0] == expected_commands[1]


@pytest.mark.parametrize(
    "args, expected_err_message",
    [
        (
            {"endpoint_id": "", "process_id": "process_id_test", "process_name": "process_name_test", "approve_action": "YES"},
            "Endpoint ID was not specified.",
        ),
        (
            {"endpoint_id": "endpoint_id_test", "process_id": "", "process_name": "", "approve_action": "YES"},
            "Process information was not specified - You should provide the process ID or the process name.",
        ),
    ],
)
def test_missing_arguments(mocker, args, expected_err_message):
    """
    Given:
        1. Demisto args object containing an empty string as the endpoint_id argument.
           An error message about missing endpoint ID.

        2. Demisto args object containing an empty string as the process_id and process_name arguments.
           An error message about missing process information.
    When:
        Running the 'main' function.

    Then:
        Verify that the expected error message is returned as an error.
    """

    import KillProcessWrapper

    mocker.patch.object(demisto, "args", return_value=args)
    return_error_mock = mocker.patch.object(KillProcessWrapper, "return_error")

    KillProcessWrapper.main()

    assert return_error_mock.call_count == 1
    assert return_error_mock.call_args[0][0] == (f"Failed to execute KillProcessWrapper. Error: {expected_err_message}")


@pytest.mark.parametrize("approve_action", ["YES", "NO"])
def test_approve_action_functionality(mocker, approve_action):
    """
    Given:
        1. Demisto args object containing an empty string as the endpoint_id argument.
           An error message about missing endpoint ID.

        2. Demisto args object containing an empty string as the process_id and process_name arguments.
           An error message about missing process information.
    When:
        Running the 'main' function.

    Then:
        Verify that the expected error message is returned as an error.
    """

    import KillProcessWrapper

    args = {
        "endpoint_id": "endpoint_id_test",
        "process_id": "process_id_test",
        "process_name": "process_name_test",
        "approve_action": approve_action,
    }

    # Create mockers:
    mocker.patch.object(demisto, "args", return_value=args)
    run_killing_process_action_mock = mocker.patch.object(KillProcessWrapper, "run_killing_process_action")
    return_results_mock = mocker.patch.object(KillProcessWrapper, "return_results")

    # Execute the main function of the script:
    KillProcessWrapper.main()

    # Assertions:
    if approve_action == "YES":
        assert run_killing_process_action_mock.call_count == 1
        assert return_results_mock.call_count == 1

    elif approve_action == "NO":
        assert run_killing_process_action_mock.call_count == 0
        assert return_results_mock.call_count == 1
        assert return_results_mock.call_args[0][0].readable_output == "The process was not killed because no approval was given."