RecordedFutureLists

Search and manage watchlists in Recorded Future.

Data Enrichment & Threat Intelligence · Recorded Future Intelligence

Details

IDRecordedFutureLists
ProviderMastercard
CategoryData Enrichment & Threat Intelligence
From Version6.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Search and manage watchlists in Recorded Future
This integration was integrated and tested with version 1.1.1 of RecordedFutureLists

Configure Recorded Future - Lists in Cortex

Parameter Description Required
API URL Default URL: https://api.recordedfuture.com/gw/xsoar/ True
API Token   True
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.

recordedfuture-lists-search


Search for lists in Recorded Future

Base Command

recordedfuture-lists-search

Input

Argument Name Description Required
list_names Freetext name to search for. Optional
contains Filter lists based on entity types, will only include lists with the entity types specified. Default value “” includes all types. Possible values are: entity, source, text, custom, ip, domain, tech_stack, industry, brand, partner, industry_peer, location, supplier, vulnerability, company, hash, operation, attacker, target, method. Optional
limit Limits the amount of returned results. Optional
include Include all search results. Default is to exclude all lists owned by the system user. Possible values are: all. Optional

Context Output

Path Type Description
RecordedFuture.List.id String Unique id of the list in Recorded Future
RecordedFuture.List.name String Name of the list in Recorded Future
RecordedFuture.List.type String Recorded future entity type
RecordedFuture.List.created String Timestamp of creation
RecordedFuture.List.updated String Timestamp of last update to the list
RecordedFuture.List.owner_id String Unique id of the owner in Recorded Future
RecordedFuture.List.owner_name String Readable name of list in Recorded Future

recordedfuture-lists-add-entities


Add entities to a list, separate entities by commas. “NOTE:” if entity type is specified, only one entity type can be added with each action.
When adding IDs use the following for Recorded Future light entities:

  • IPaddress: “ip:x.x.x.x”
  • Domain: “idn:example.xyz”
  • Hash: “hash:examplehashvalue”
  • Email: “email:example@example.xyz”
  • Url: “url:https://example.xyz”

Base Command

recordedfuture-lists-add-entities

Input

Argument Name Description Required
list_id Id of the list that should be added, can be found by running !recordedfuture-lists-search with the corresponding filters or in the Recorded Future portal. Required
entity_ids Specific ids from Recorded Future separated by comma, For urls containing commas: replace comma with %2C. Optional
freetext_names Freetext names will be matched to Recorded Future ids separated by comma, this alernative will add the best match in the Recorded Future data. For urls containing commas: escape with %2C. Optional
entity_type Type of the entities that should be added. Use together with freetext_names to improve entity resolution. Possible values are: ip, domain, malware, url, hash, cve, company, person, product, industry, country, attack-vector, operation, mitre-identifier, malware-category. Optional

Context Output

Path Type Description
RecordedFuture.List.Entities.name String Name of the entity in the list
RecordedFuture.List.Entities.type String The Recorded Future entity type resolved during the action
RecordedFuture.List.Entities.id String Unique id of the entity in Recorded Future
RecordedFuture.List.Entities.input_value String The value inputted to the command
RecordedFuture.List.Entities.action_result String Entity specific result for the action

recordedfuture-lists-remove-entities


Remove entities from a list. Separate entities with commas. “NOTE:” If entity type is specified, only one entity type can be added with each action.
When adding IDs use the following for Recorded Future light entities:

  • IPaddress: “ip:x.x.x.x”
  • Domain: “idn:example.xyz”
  • Hash: “hash:examplehashvalue”
  • Email: “email:example@example.xyz”
  • Url: “url:https://example.xyz”

Base Command

recordedfuture-lists-remove-entities

Input

Argument Name Description Required
list_id ID of the list that should be removed. Can be found by running !recordedfuture-lists-search with the corresponding filters or in the Recorded Future portal. Required
entity_ids A comma-separated list of specific IDs from Recorded Future. For URLs containing commas, replace comma with %2C. Optional
freetext_names A comma-separated list of freetext names to be matched to Recorded Future IDs. This will remove the best match in the Recorded Future data. For URLs containing commas, escape with %2C. Optional
entity_type Type of the entities that should be removed. Use together with freetext_names to improve entity resolution. Possible values are: ip, domain, malware, url, hash, cve, company, person, product, industry, country, attack-vector, operation, mitre-identifier, malware-category. Optional

Context Output

Path Type Description
RecordedFuture.List.Entities.name String Name of the entity in the list
RecordedFuture.List.Entities.type String The Recorded Future entity type resolved during the action.
RecordedFuture.List.Entities.id String Unique ID of the entity in Recorded Future.
RecordedFuture.List.Entities.input_value String The value inputted to the command.
RecordedFuture.List.Entities.action_result String Entity specific result for the action.

recordedfuture-lists-entities


Get the entities that are currently in the given lists.

Base Command

recordedfuture-lists-entities

Input

Argument Name Description Required
list_ids A comma-separated list of Recorded Future list IDs. Required

Context Output

Path Type Description
RecordedFuture.List.id String Unique ID of the list in Recorded Future.
RecordedFuture.List.name String Name of the list in Recorded Future.
RecordedFuture.List.type String Recorded Future entity type.
RecordedFuture.List.Entities.name String Name of the entity in the list.
RecordedFuture.List.Entities.type String The Recorded Future entity type resolved during the action.
RecordedFuture.List.Entities.id String Unique ID of the entity in Recorded Future.

Configuration parameters

  • server_url — API URL (required)
  • token — (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (4)

  • recordedfuture-lists-add-entities

    Add entities to a list, separate entities by commas. "NOTE:" if entity type is specified, only one entity type can be added with each action.

  • recordedfuture-lists-entities

    Get the entities that are currently in the given lists.

  • recordedfuture-lists-remove-entities

    Remove entities from a list. Separate entities with a comma. "NOTE:" If entity type is specified, only one entity type can be added with each action.

  • recordedfuture-lists-search

    Search for lists in Recorded Future.

from pytest import raises  # noqa: PT013


def create_client():
    import os
    from RecordedFutureLists import Client, __version__

    base_url = "https://api.recordedfuture.com/gw/xsoar/"
    verify_ssl = True
    token = os.environ.get("RF_TOKEN")
    headers = {
        "X-RFToken": token,
        "X-RF-User-Agent": f"RecordedFutureLists.py/{__version__} (Linux-5.13.0-1031-aws-x86_64-with) "
        f"XSOAR/{__version__} RFClient/{__version__} (Cortex_XSOAR_6.5.0)",
    }

    return Client(base_url=base_url, verify=verify_ssl, headers=headers, proxy=False)


class TestRFClient:
    def test_whoami(self, mocker):
        client = create_client()

        mock_http_request = mocker.patch.object(client, "_http_request")

        client.whoami()

        mock_http_request.assert_called_once_with(
            method="get",
            url_suffix="info/whoami",
            timeout=60,
        )

    def test_call_with_kwargs(self, mocker):
        """
        Test _call() with kwargs.
        """

        import os
        import demistomock as demisto

        STATUS_TO_RETRY = [500, 501, 502, 503, 504]

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        # Mock demisto command and args.
        mock_command_name = "command_name"
        mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_http_request = mocker.patch.object(client, "_http_request")

        mock_url_suffix = "mock_url_suffix"

        client._call(url_suffix=mock_url_suffix, timeout=120, any_other_kwarg=True)

        json_data = {
            "demisto_command": mock_command_name,
            "demisto_args": mock_command_args,
        }

        mock_http_request.assert_called_once_with(
            method="post",
            url_suffix=mock_url_suffix,
            json_data=json_data,
            timeout=120,
            retries=3,
            status_list_to_retry=STATUS_TO_RETRY,
            any_other_kwarg=True,
        )

    def test_call_returns_response(self, mocker):
        """
        Test _call() returns response.
        """

        import os
        import demistomock as demisto

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        # Mock demisto command and args.
        mock_command_name = "command_name"
        mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_response = {"response": {"data": "mock data"}}

        mocker.patch.object(client, "_http_request", return_value=mock_response)

        mock_url_suffix = "mock_url_suffix"

        response = client._call(url_suffix=mock_url_suffix)
        assert response == mock_response

    def test_call_response_processing_return_error(self, mocker):
        """
        Test _call() return_error response processing.
        """

        import os
        import demistomock as demisto

        STATUS_TO_RETRY = [500, 501, 502, 503, 504]

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        # Mock demisto command and args.
        mock_command_name = "command_name"
        mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        mock_return_error = mocker.patch("RecordedFutureLists.return_error")

        client = create_client()

        mock_http_request = mocker.patch.object(
            client,
            "_http_request",
            return_value={"return_error": {"message": "mock error"}},
        )

        mock_url_suffix = "mock_url_suffix"

        client._call(url_suffix=mock_url_suffix)

        json_data = {
            "demisto_command": mock_command_name,
            "demisto_args": mock_command_args,
        }

        mock_http_request.assert_called_once_with(
            method="post",
            url_suffix=mock_url_suffix,
            json_data=json_data,
            timeout=90,
            retries=3,
            status_list_to_retry=STATUS_TO_RETRY,
        )

        mock_return_error.assert_called_once_with(message="mock error")

    def test_call_raising_demisto_exception(self, mocker):
        from CommonServerPython import DemistoException
        import demistomock as demisto

        mock_command_name = "command_name"
        mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)
        client = create_client()
        mocked_http_request = mocker.patch.object(client, "_http_request")
        mocked_http_request.side_effect = DemistoException("Some exception as a side effect")
        raises(DemistoException, client._call, "mocked")

    def test_call_response_processing_404(self, mocker):
        """
        Test _call() response processing.
        """

        import os
        import demistomock as demisto
        from CommonServerPython import DemistoException, CommandResults

        STATUS_TO_RETRY = [500, 501, 502, 503, 504]

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        # Mock demisto command and args.
        mock_command_name = "command_name"
        mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        mocker.patch("RecordedFutureLists.return_error")

        client = create_client()

        def mock_http_request_method(*args, **kwargs):
            # Imitate how CommonServerPython handles bad responses (when status code not in ok_codes,
            # or if ok_codes=None - it uses requests.Response.ok to check whether response is good).
            raise DemistoException("404")

        mocker.patch.object(client, "_http_request", mock_http_request_method)

        spy_http_request = mocker.spy(client, "_http_request")

        mock_url_suffix = "mock_url_suffix"

        result = client._call(url_suffix=mock_url_suffix)

        json_data = {
            "demisto_command": mock_command_name,
            "demisto_args": mock_command_args,
        }

        spy_http_request.assert_called_once_with(
            method="post",
            url_suffix=mock_url_suffix,
            json_data=json_data,
            timeout=90,
            retries=3,
            status_list_to_retry=STATUS_TO_RETRY,
        )

        assert isinstance(result, CommandResults)

        assert result.outputs_prefix == ""
        assert result.outputs_key_field == ""
        assert result.outputs == {}
        assert result.raw_response == {}
        assert result.readable_output == "No results found."

    def test_list_search(self, mocker):
        import os
        import demistomock as demisto

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        # Mock demisto command and args.
        mock_command_name = "command_name"
        mock_command_args = {
            "list_names": "arg1_value,arg1_value2",
            "contains": "arg2_value",
        }
        mock_call_args = {
            "list_names": ["arg1_value", "arg1_value2"],
            "contains": ["arg2_value"],
        }

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_call_response = {"response": {"data": "mock response"}}
        mock_call = mocker.patch.object(client, "_call", return_value=mock_call_response)

        response = client.list_search()

        mock_call.assert_called_once_with(demisto_args=mock_call_args, url_suffix="/v2/lists/search")

        assert response == mock_call_response

    def test_entites_fetch(self, mocker):
        import os
        import demistomock as demisto

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        # Mock demisto command and args.
        mock_command_name = "command_name"
        mock_command_args = {
            "list_ids": "arg1_value,arg1_value2",
        }
        mock_call_args = {"list_ids": ["arg1_value", "arg1_value2"]}

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_call_response = {"response": {"data": "mock response"}}
        mock_call = mocker.patch.object(client, "_call", return_value=mock_call_response)

        response = client.entity_fetch()

        mock_call.assert_called_once_with(demisto_args=mock_call_args, url_suffix="/v2/lists/entities/lookup")

        assert response == mock_call_response

    def test_entity_add_freetext(self, mocker):
        import os
        import demistomock as demisto

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        mocked_list_id = "mockvalue"
        mocked_entity_type = "mock"
        mocked_entity_ids = ""
        mocked_freetext_names = "mockedmalwarename"
        mock_command_args = {
            "list_id": mocked_list_id,
            "entity_types": mocked_entity_type,
            "entity_ids": mocked_entity_ids,
            "freetext_names": mocked_freetext_names,
        }
        # Mock demisto command and args.
        mock_command_name = "command_name"

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_call_response = {"response": {"data": "mock response"}}
        mock_call = mocker.patch.object(client, "_call", return_value=mock_call_response)

        response = client.entity_operation(operation="add")
        mock_call.assert_called_once_with(
            demisto_args=mock_command_args,
            url_suffix=f"/v2/lists/{mocked_list_id}/entities/add",
        )

        assert response == mock_call_response

    def test_entity_add_ids(self, mocker):
        import os
        import demistomock as demisto

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        mocked_list_id = "mockvalue"
        mocked_entity_type = "mock"
        mocked_entity_ids = "mockedmalwarename"
        mocked_freetext_names = ""
        mock_command_args = {
            "list_id": mocked_list_id,
            "entity_type": mocked_entity_type,
            "entity_ids": mocked_entity_ids,
            "freetext_names": mocked_freetext_names,
        }
        # Mock demisto command and args.
        mock_command_name = "command_name"

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_call_response = {"response": {"data": "mock response"}}
        mock_call = mocker.patch.object(client, "_call", return_value=mock_call_response)

        response = client.entity_operation(operation="add")
        mock_call.assert_called_once_with(
            demisto_args=mock_command_args,
            url_suffix=f"/v2/lists/{mocked_list_id}/entities/add",
        )

        assert response == mock_call_response

    def test_entity_remove_ids(self, mocker):
        import os
        import demistomock as demisto

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        mocked_list_id = "mockvalue"
        mocked_entity_type = "mock"
        mocked_entity_ids = "mockedmalwarename"
        mocked_freetext_names = ""
        mock_command_args = {
            "list_id": mocked_list_id,
            "entity_type": mocked_entity_type,
            "entity_ids": mocked_entity_ids,
            "freetext_names": mocked_freetext_names,
        }
        # Mock demisto command and args.
        mock_command_name = "command_name"

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_call_response = {"response": {"data": "mock response"}}
        mock_call = mocker.patch.object(client, "_call", return_value=mock_call_response)

        response = client.entity_operation(operation="remove")
        mock_call.assert_called_once_with(
            demisto_args=mock_command_args,
            url_suffix=f"/v2/lists/{mocked_list_id}/entities/remove",
        )

        assert response == mock_call_response

    def test_entity_add_invalid_both(self, mocker):
        import os
        import demistomock as demisto

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        mocked_list_id = "mockvalue"
        mocked_entity_type = "mock"
        mocked_entity_ids = "mockedmalwarename"
        mocked_freetext_names = "mockedmalwarename"
        mock_command_args = {
            "list_id": mocked_list_id,
            "entity_type": mocked_entity_type,
            "entity_ids": mocked_entity_ids,
            "freetext_names": mocked_freetext_names,
        }
        # Mock demisto command and args.
        mock_command_name = "command_name"

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_call_response = {"response": {"data": "mock response"}}
        mocker.patch.object(client, "_call", return_value=mock_call_response)
        raises(ValueError, client.entity_operation, operation="add")

    def test_entity_add_invalid_none(self, mocker):
        import os
        import demistomock as demisto

        # This is needed for CommonServerPython module to not add demisto.params() into callingContext.
        os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"

        mocked_list_id = "mockvalue"
        mocked_entity_type = "mock"
        mocked_entity_ids = ""
        mocked_freetext_names = ""
        mock_command_args = {
            "list_id": mocked_list_id,
            "entity_types": mocked_entity_type,
            "entity_ids": mocked_entity_ids,
            "freetext_names": mocked_freetext_names,
        }
        # Mock demisto command and args.
        mock_command_name = "command_name"

        mocker.patch.object(demisto, "command", return_value=mock_command_name)
        mocker.patch.object(demisto, "args", return_value=mock_command_args)

        client = create_client()

        mock_call_response = {"response": {"data": "mock response"}}
        mocker.patch.object(client, "_call", return_value=mock_call_response)

        raised = False
        try:
            client.entity_operation(operation="add")
        except ValueError:
            raised = True
        assert raised


class TestActions:
    def test_init(self, mocker):
        from RecordedFutureLists import Actions

        mock_client = mocker.Mock()
        actions = Actions(mock_client)
        assert actions.client == mock_client

    def test_process_result_actions_404(self, mocker):
        from RecordedFutureLists import Actions
        from CommonServerPython import CommandResults

        mock_client = mocker.Mock()
        actions = Actions(mock_client)

        # Test if response is CommandResults
        # (case when we got 404 on response, and it was processed in self.client._call() method).
        response = CommandResults(readable_output="Mock")
        result_actions = actions._process_result_actions(response=response)
        assert result_actions == [response]

    def test_process_result_actions_response_is_not_dict(self, mocker):
        from RecordedFutureLists import Actions

        mock_client = mocker.Mock()
        actions = Actions(mock_client)

        # Test if response is not CommandResults and not Dict.
        response = "Mock string - not CommandResults and not dict"
        result_actions = actions._process_result_actions(response=response)  # type: ignore
        assert result_actions is None

    def test_process_result_actions_no_or_empty_result_actions_in_response(self, mocker):
        from RecordedFutureLists import Actions

        mock_client = mocker.Mock()
        actions = Actions(mock_client)

        # Test no results_actions in response.
        response = {"data": "mock"}
        result_actions = actions._process_result_actions(response=response)
        assert result_actions is None

        # Test case when bool(results_actions) in response is False.
        response = {"data": "mock", "result_actions": None}
        result_actions = actions._process_result_actions(response=response)
        assert result_actions is None

        response = {"data": "mock", "result_actions": []}
        result_actions = actions._process_result_actions(response=response)
        assert result_actions is None

        response = {"data": "mock", "result_actions": {}}
        result_actions = actions._process_result_actions(response=response)
        assert result_actions is None

    def test_process_result_actions_command_results_only(self, mocker):
        from RecordedFutureLists import Actions, CommandResults

        mock_client = mocker.Mock()
        actions = Actions(mock_client)

        response = {
            "data": "mock",
            "result_actions": [
                {
                    "CommandResults": {
                        "outputs_prefix": "mock_outputs_prefix",
                        "outputs": "mock_outputs",
                        "raw_response": "mock_raw_response",
                        "readable_output": "mock_readable_output",
                        "outputs_key_field": "mock_outputs_key_field",
                    },
                }
            ],
        }
        result_actions = actions._process_result_actions(response=response)

        assert len(result_actions) == 1

        r_a = result_actions[0]

        assert isinstance(r_a, CommandResults)

        assert r_a.outputs_prefix == "mock_outputs_prefix"
        assert r_a.outputs == "mock_outputs"
        assert r_a.raw_response == "mock_raw_response"
        assert r_a.readable_output == "mock_readable_output"
        assert r_a.outputs_key_field == "mock_outputs_key_field"

    def test_list_search_command_without_result_actions(self, mocker):
        from RecordedFutureLists import Actions

        client = create_client()

        mock_response = "mock_response"

        mock_client_lists_list_search = mocker.patch.object(client, "list_search", return_value=mock_response)

        actions = Actions(client)

        mock_process_result_actions_return_value = None
        mock_process_result_actions = mocker.patch.object(
            actions,
            "_process_result_actions",
            return_value=mock_process_result_actions_return_value,
        )

        actions.list_search_command()

        mock_client_lists_list_search.assert_called_once_with()

        mock_process_result_actions.assert_called_once_with(response=mock_response)

    def test_list_search_command_with_result_actions(self, mocker):
        from RecordedFutureLists import Actions

        client = create_client()

        mock_response = "mock_response"

        mock_client_lists_list_search = mocker.patch.object(client, "list_search", return_value=mock_response)

        actions = Actions(client)

        mock_process_result_actions_return_value = "mocked_process_return_value"
        mock_process_result_actions = mocker.patch.object(
            actions,
            "_process_result_actions",
            return_value=mock_process_result_actions_return_value,
        )

        result = actions.list_search_command()

        mock_client_lists_list_search.assert_called_once_with()

        mock_process_result_actions.assert_called_once_with(response=mock_response)

        assert result == mock_process_result_actions_return_value

    def test_list_entities_command_with_result_actions(self, mocker):
        from RecordedFutureLists import Actions

        client = create_client()

        mock_response = "mock_response"

        mock_client_lists_list_search = mocker.patch.object(client, "entity_fetch", return_value=mock_response)

        actions = Actions(client)

        mock_process_result_actions_return_value = "mocked_process_return_value"
        mock_process_result_actions = mocker.patch.object(
            actions,
            "_process_result_actions",
            return_value=mock_process_result_actions_return_value,
        )

        result = actions.entities_get_command()

        mock_client_lists_list_search.assert_called_once_with()

        mock_process_result_actions.assert_called_once_with(response=mock_response)

        assert result == mock_process_result_actions_return_value

    def test_entity_add_command_with_result_action(self, mocker):
        from RecordedFutureLists import Actions

        client = create_client()

        mock_response = "mock_response"

        mock_client_entity_add = mocker.patch.object(client, "entity_operation", return_value=mock_response)

        actions = Actions(client)

        mock_process_result_actions_return_value = "mock_process_result_actions_return_value"
        mock_process_result_actions = mocker.patch.object(
            actions,
            "_process_result_actions",
            return_value=mock_process_result_actions_return_value,
        )

        result = actions.entity_add_command()

        mock_client_entity_add.assert_called_once_with("add")

        mock_process_result_actions.assert_called_once_with(response=mock_response)

        assert result == mock_process_result_actions_return_value

    def test_entity_remove_command_with_result_action(self, mocker):
        from RecordedFutureLists import Actions

        client = create_client()

        mock_response = "mock_response"

        mock_client_entity_add = mocker.patch.object(client, "entity_operation", return_value=mock_response)

        actions = Actions(client)

        mock_process_result_actions_return_value = "mock_process_result_actions_return_value"
        mock_process_result_actions = mocker.patch.object(
            actions,
            "_process_result_actions",
            return_value=mock_process_result_actions_return_value,
        )

        result = actions.entity_remove_command()

        mock_client_entity_add.assert_called_once_with("remove")

        mock_process_result_actions.assert_called_once_with(response=mock_response)

        assert result == mock_process_result_actions_return_value

    def test_test_module(self, mocker):
        import RecordedFutureLists
        import demistomock as demisto
        import platform

        mocker.patch.object(demisto, "command", return_value="test-module")
        mocker.patch.object(demisto, "demistoVersion", return_value={"version": "mock_version"})
        mocker.patch.object(demisto, "params", return_value={"token": {"password": "mocktoken"}})
        mocker.patch.object(platform, "platform", return_value="mock_platform")
        mocker.patch.object(RecordedFutureLists.Client, "whoami")
        mocked_return_res = mocker.patch.object(RecordedFutureLists, "return_results")
        RecordedFutureLists.main()
        mocked_return_res.assert_called_with("ok")

    def test_test_module_with_boom(self, mocker):
        import RecordedFutureLists
        import demistomock as demisto
        import platform

        mocker.patch.object(demisto, "command", return_value="test-module")
        mocker.patch.object(demisto, "demistoVersion", return_value={"version": "mock_version"})
        mocker.patch.object(demisto, "params", return_value={"token": {"password": "mocktoken"}})
        mocker.patch.object(platform, "platform", return_value="mock_platform")
        mock_whoami = mocker.patch.object(RecordedFutureLists.Client, "whoami")
        mock_whoami.side_effect = Exception("Side effect triggered")
        mocked_return_err = mocker.patch.object(RecordedFutureLists, "return_error")
        RecordedFutureLists.main()
        mocked_return_err.assert_called_with(
            message=(
                f"Failed to execute {demisto.command()} command: Failed due to - "
                "Unknown error. Please verify that the API URL and Token are correctly configured. "
                "RAW Error: Side effect triggered"
            )
        )