CrowdStrike Indicator Feed

Retrieves indicators from the CrowdStrike Falcon Intel Feed.

Data Enrichment & Threat Intelligence · Crowdstrike Falcon Intel Feed · Feed

Details

IDCrowdStrike Indicator Feed
ProviderCrowdStrike
CategoryData Enrichment & Threat Intelligence
From Version5.5.0
Docker Imagedemisto/python3:3.12.13.10116658

README

CrowdStrike Falcon Intel Indicator Feed

Configure CrowdStrike Indicator Feed in Cortex

Parameter Description Required
Fetch indicators   False
CrowdStrike Base URL   True
CrowdStrike API Client ID For non 6.1 - enter your CrowdStrike API Client Secret in the password field. True
Type The indicator types to fetch. Out-of-the-box indicator types supported in XSOAR are: “Account”, “Domain”, “Email”, “File MD5”, “File SHA256”, “IP”, “Registry Key”, and “URL”. The default is “ALL”. False
First fetch time The time range to consider for the initial data fetch. Leave empty to fetch from the first available indicator. False
Max. indicators per fetch Maximum number of indicators per fetch. Value should be between 1 - 10000. A large value may result in a timeout. False
Malicious confidence Malicious confidence level to filter by. False
Include deleted indicators   False
Filter Advanced: FQL query. For more information visit the CrowdStrike documentation. For example: published_date:>”now-3d” can be used to only pull indicators published in the last 3 days. False
Generic phrase match Generic phrase match search across all indicator fields. False
Indicator Reputation Indicators from this integration instance will be marked with this reputation. False
Source Reliability Reliability of the source providing the intelligence data. True
Traffic Light Protocol Color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. False
Indicator Expiration Method The feed’s expiration method. False
Feed Fetch Interval The interval after which the feed expires. False
Tags Supports CSV values. False
Trust any certificate (not secure)   False
Use system proxy settings   False
Bypass exclusion list When selected, the exclusion list is ignored for indicators from this feed. This means that if an indicator from this feed is on the exclusion list, the indicator might still be added to the system. False

Note: To change the fetch start time , use the crowdstrike-reset-fetch-indicators command after setting the desired time in First Fetch Time parameter.

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.

crowdstrike-indicators-list


Gets indicators from the CrowdStrike Falcon Intel Feed.

Base Command

crowdstrike-indicators-list

Input

Argument Name Description Required
limit The maximum number of indicators to return. Default is 50. Optional
offset The index of the first indicator to fetch. Optional

Context Output

Path Type Description
CrowdStrikeFalconIntel.Indicators.id String Indicator ID.
CrowdStrikeFalconIntel.Indicators.value String Indicator value.
CrowdStrikeFalconIntel.Indicators.type String Indicator type.
CrowdStrikeFalconIntel.Indicators.fields.reports Unknown Indicator reports.
CrowdStrikeFalconIntel.Indicators.fields.actors Unknown Actors related to the indicator.
CrowdStrikeFalconIntel.Indicators.fields.malwarefamily Unknown Indicator malware families.
CrowdStrikeFalconIntel.Indicators.fields.stixkillchainphases Unknown Indicator kill chains.
CrowdStrikeFalconIntel.Indicators.fields.maliciousconfidence String Indicator malicious confidence.
CrowdStrikeFalconIntel.Indicators.fields.tags Unknown Indicator labels.
CrowdStrikeFalconIntel.Indicators.fields.targets Unknown Targets of the indicator.
CrowdStrikeFalconIntel.Indicators.fields.threattypes Unknown Indicator threat types.
CrowdStrikeFalconIntel.Indicators.fields.vulnerabilities Unknown Indicator vulnerabilities.
CrowdStrikeFalconIntel.Indicators.fields.ipaddress Unknown Indicator related IP address.
CrowdStrikeFalconIntel.Indicators.fields.domainname Unknown Indicator related domains.
CrowdStrikeFalconIntel.Indicators.fields.updateddate Date Indicator update date.
CrowdStrikeFalconIntel.Indicators.fields.creationdate Unknown Indicator creation date.
CrowdStrikeFalconIntel.Indicators.rawJSON Unknown Raw response.

Command Example

!crowdstrike-indicators-list limit=3

Context Example

{
  "CrowdStrikeFalconIntel": {
    "Indicators": [
      {
        "fields": {
          "actor": [],
          "creationdate": 1600080520,
          "domainname": [],
          "ipaddress": [],
          "confidence": "low",
          "malwarefamily": [
            "Remcos"
          ],
          "reports": [],
          "stixkillchainphases": [
            "C2"
          ],
          "threattypes": [
            {
              "threatcategory": "Criminal"
            }
          ],
          "tags": [
            "MaliciousConfidence/Low",
            "KillChain/C2",
            "ThreatType/Commodity",
            "ThreatType/Criminal",
            "ThreatType/CredentialHarvesting",
            "Malware/Remcos"
          ],
          "targets": [],
          "trafficlightprotocol": "AMBER",
          "updateddate": 1608207378,
          "vulnerabilities": []
        },
        "id": "ip_address_1.1.1.1",
        "rawJSON": {
          "_marker": "1608207378159fc77935511a2f0c9541511bd936f8",
          "actors": [],
          "deleted": false,
          "domain_types": [],
          "id": "ip_address_1.1.1.1",
          "indicator": "1.1.1.1",
          "ip_address_types": [],
          "kill_chains": [
            "C2"
          ],
          "labels": [
            {
              "created_on": 1600080520,
              "last_valid_on": 1608207377,
              "name": "MaliciousConfidence/Low"
            },
            {
              "created_on": 1600080520,
              "last_valid_on": 1608207377,
              "name": "KillChain/C2"
            }
          ],
          "last_updated": 1608207378,
          "malicious_confidence": "low",
          "malware_families": [
            "Remcos"
          ],
          "published_date": 1600080520,
          "relations": [
            {
              "created_date": 1608207377,
              "id": "hash_sha256_9bb12d611cb19e84f2f22791cb86a43841e95020b1e113469e5cad95b97a8d42",
              "indicator": "9bb12d611cb19e84f2f22791cb86a43841e95020b1e113469e5cad95b97a8d42",
              "last_valid_date": 1608207377,
              "type": "hash_sha256"
            },
            {
              "created_date": 1608207377,
              "id": "hash_sha256_58a3e65de35d8da1f7955680e07a82ede43a1e677e0abc200923b484a7615494",
              "indicator": "58a3e65de35d8da1f7955680e07a82ede43a1e677e0abc200923b484a7615494",
              "last_valid_date": 1608207377,
              "type": "hash_sha256"
            }
          ],
          "reports": [],
          "targets": [],
          "threat_types": [
            "Criminal"
          ],
          "type": "ip_address",
          "vulnerabilities": []
        },
        "type": "IP",
        "value": "1.1.1.1"
      },
      {
        "fields": {
          "actor": [],
          "creationdate": 1608208087,
          "domainname": [],
          "ipaddress": [],
          "confidence": "low",
          "malwarefamily": [
            "Remcos"
          ],
          "reports": [],
          "stixkillchainphases": [
            "C2"
          ],
          "tags": [
            "MaliciousConfidence/Low",
            "KillChain/C2",
            "Malware/Remcos",
            "ThreatType/Commodity",
            "ThreatType/Criminal",
            "ThreatType/CredentialHarvesting"
          ],
          "threattypes": [
            {
              "threatcategory": "Criminal"
            }
          ],
          "targets": [],
          "trafficlightprotocol": "AMBER",
          "updateddate": 1608208109,
          "vulnerabilities": []
        },
        "id": "ip_address_2.2.2.2",
        "rawJSON": {
          "_marker": "16082081092644654ac0f7738b7086d25532d38ec1",
          "actors": [],
          "deleted": false,
          "domain_types": [],
          "id": "ip_address_2.2.2.2",
          "indicator": "2.2.2.2",
          "ip_address_types": [],
          "kill_chains": [
            "C2"
          ],
          "labels": [
            {
              "created_on": 1608208087,
              "last_valid_on": 1608208108,
              "name": "MaliciousConfidence/Low"
            },
            {
              "created_on": 1608208087,
              "last_valid_on": 1608208108,
              "name": "KillChain/C2"
            }
          ],
          "last_updated": 1608208109,
          "malicious_confidence": "low",
          "malware_families": [
            "Remcos"
          ],
          "published_date": 1608208087,
          "relations": [
            {
              "created_date": 1608208090,
              "id": "hash_sha256_b90713f3b31f29ceb64355b3c016aa0a74e1ce90dca5570db04aff27e12b343c",
              "indicator": "b90713f3b31f29ceb64355b3c016aa0a74e1ce90dca5570db04aff27e12b343c",
              "last_valid_date": 1608208090,
              "type": "hash_sha256"
            },
            {
              "created_date": 1483468884,
              "id": "domain_holmann02.ddns.net",
              "indicator": "holmann02.ddns.net",
              "last_valid_date": 1483468884,
              "type": "domain"
            }
          ],
          "reports": [],
          "targets": [],
          "threat_types": [
            "Criminal"
          ],
          "type": "ip_address",
          "vulnerabilities": []
        },
        "type": "IP",
        "value": "1.2.3.4"
      },
      {
        "fields": {
          "actor": [
            "MUMMYSPIDER"
          ],
          "creationdate": 1592473928,
          "domainname": [],
          "ipaddress": [],
          "confidence": "low",
          "malwarefamily": [],
          "reports": [],
          "stixkillchainphases": [
            "C2"
          ],
          "threattypes": [],
          "tags": [
            "KillChain/C2",
            "MaliciousConfidence/Low",
            "Actor/MUMMYSPIDER"
          ],
          "targets": [],
          "trafficlightprotocol": "AMBER",
          "updateddate": 1608208626,
          "vulnerabilities": []
        },
        "id": "ip_address_1.2.3.4",
        "rawJSON": {
          "_marker": "1608208626d02e40678e554f71fd6c3c33cc71c5c0",
          "actors": [
            "MUMMYSPIDER"
          ],
          "deleted": false,
          "domain_types": [],
          "id": "ip_address_1.2.3.4",
          "indicator": "1.2.3.4",
          "ip_address_types": [],
          "kill_chains": [
            "C2"
          ],
          "labels": [
            {
              "created_on": 1592473928,
              "last_valid_on": 1592473930,
              "name": "KillChain/C2"
            },
            {
              "created_on": 1592473928,
              "last_valid_on": 1592473930,
              "name": "MaliciousConfidence/Low"
            },
            {
              "created_on": 1592473930,
              "last_valid_on": 1592473930,
              "name": "Actor/MUMMYSPIDER"
            }
          ],
          "last_updated": 1608208626,
          "malicious_confidence": "low",
          "malware_families": [],
          "published_date": 1592473928,
          "relations": [
            {
              "created_date": 1597858281,
              "id": "url_http://1.1.1.1:80",
              "indicator": "http://1.1.1.1:80",
              "last_valid_date": 1597858281,
              "type": "url"
            },
            {
              "created_date": 1592473931,
              "id": "hash_md5_6d795170965336a9006f059dd444fc8f",
              "indicator": "6d795170965336a9006f059dd444fc8f",
              "last_valid_date": 1592473931,
              "type": "hash_md5"
            }
          ],
          "reports": [],
          "targets": [],
          "threat_types": [],
          "type": "ip_address",
          "vulnerabilities": []
        },
        "type": "IP",
        "value": "1.2.3.4"
      }
    ]
  }
}

Human Readable Output

Indicators from CrowdStrike Falcon Intel

Type Value Id
IP 1.1.1.1 ip_address_1.1.1.1
IP 2.2.2.2 ip_address_2.2.2.2
IP 1.2.3.4 ip_address_1.2.3.4

crowdstrike-reset-fetch-indicators


Resets the retrieving start time according to the First Fetch Time parameter, WARNING: This command will reset your fetch history.

Base Command

crowdstrike-reset-fetch-indicators

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command Example

!crowdstrike-reset-fetch-indicators

Human Readable Output

Fetch history deleted successfully

Configuration parameters

  • feed — Fetch indicators
  • base_url — CrowdStrike Base URL (required)
  • credentials — CrowdStrike API Client ID (required)
  • timeout — The amount of time (in seconds) that a request will wait for a client to establish a connection to a remote machine before a timeout occurs.
  • type — Type
  • first_fetch — First fetch time (required)
  • max_indicator_to_fetch — Max. indicators per fetch
  • malicious_confidence — Malicious confidence
  • include_deleted — Include deleted indicators
  • filter — Filter
  • generic_phrase — Generic phrase match
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability (required)
  • tlp_color — Traffic Light Protocol Color
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedFetchInterval — Feed Fetch Interval
  • feedTags — Tags
  • create_relationships — Create relationships
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • feedBypassExclusionList — Bypass exclusion list
  • feedIncremental — Incremental Feed

Commands (2)

  • crowdstrike-indicators-list

    Gets indicators from the CrowdStrike Falcon Intel Feed.

  • crowdstrike-reset-fetch-indicators

    Resets the retrieving start time according to the `First Fetch Time` parameter, WARNING: This command will reset your fetch history.

import json

import demistomock as demisto
import pytest


def util_load_json(path):
    with open(path, encoding="utf-8") as f:
        return json.loads(f.read())


def test_crowdstrike_indicators_list_command(requests_mock):
    """Tests crowdstrike_indicators_list_command function
    Given
        - The following indicator type: 'domain' that was chosen by the user.
        - include_deleted: False
    When
        - Calling `crowdstrike_indicators_list_command`
    Then
        - convert the result to indicators list
        - validate the length of the indicators list
        - validate the Feed tags is as expected
    """

    from CrowdStrikeIndicatorFeed import Client, crowdstrike_indicators_list_command

    mock_response = util_load_json("test_data/crowdstrike_indicators_list_command.json")
    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    requests_mock.get(url="https://api.crowdstrike.com/intel/combined/indicators/v1", json=mock_response)
    requests_mock.get(
        url="https://api.crowdstrike.com/intel/entities/actors/v1?ids=GOBLINPANDA&fields=name",
        json={"resources": [{"name": "GOBLIN PANDA"}]},
    )
    feed_tags = ["Tag1", "Tag2"]
    client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="Domain",
        include_deleted="false",
        limit=2,
        feed_tags=feed_tags,
    )
    args = {"limit": "2"}
    response = crowdstrike_indicators_list_command(client, args)
    assert len(response.outputs) == 3
    assert len(response.raw_response) == 3
    assert "Indicators from CrowdStrike Falcon Intel" in response.readable_output
    assert "domain_abc" in response.readable_output
    assert feed_tags[0]
    assert feed_tags[1] in response.raw_response[0]["fields"]["tags"]


@pytest.mark.parametrize(
    "types_list, expected",
    [
        (
            ["ALL"],
            "type:'username',type:'domain',type:'email_address',type:'hash_md5',type:'hash_sha1',"
            "type:'hash_sha256',type:'registry',type:'url',type:'ip_address',type:'reports',type:'actors',"
            "type:'malware_families',type:'vulnerabilities'",
        ),
        (["Domain", "Email", "Registry Key"], "type:'domain',type:'email_address',type:'registry'"),
    ],
)
def test_build_type_fql(types_list, expected):
    """Tests build_type_fql function
    Given
        - Indicator types that were chosen by the user.
    When
        - Calling `build_type_fql` in order to build filter for `get_indicators`
    Then
        - validate result as expected
    """
    from CrowdStrikeIndicatorFeed import Client

    res = Client.build_type_fql(types_list=types_list)
    assert res == expected


def test_create_indicators_from_response(requests_mock, mocker):
    """Tests build_type_fql function
    Given
        - Indicator types that were chosen by the user.
    When
        - Calling `create_indicators_from_response` in order to build indicators from response
    Then
        - validate result as expected
    """
    from CrowdStrikeIndicatorFeed import Client

    mocker.patch("CrowdStrikeIndicatorFeed.get_integration_context", return_value={"GOBLINPANDA": "GOBLIN PANDA"})
    raw_response = util_load_json("test_data/crowdstrike_indicators_list_command.json")
    expected_result = util_load_json("test_data/create_indicators_from_response.json")
    res = Client.create_indicators_from_response(raw_response, Client.get_actors_names_request)
    assert res == expected_result


def test_empty_first_fetch(mocker, requests_mock):
    mocker.patch.object(demisto, "params", return_value={"first_fetch": ""})
    mocker.patch.object(demisto, "command", return_value="")
    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    from CrowdStrikeIndicatorFeed import main

    main()
    assert True


@pytest.mark.parametrize(
    "field, indicator, resource, expected_results",
    [
        (
            "relations",
            {"type": "hash_md5", "value": "1234567890"},
            {"relations": [{"type": "password"}, {"type": "username", "indicator": "abc"}]},
            [
                {
                    "name": "related-to",
                    "reverseName": "related-to",
                    "type": "IndicatorToIndicator",
                    "entityA": "1234567890",
                    "entityAFamily": "Indicator",
                    "entityAType": "hash_md5",
                    "entityB": "abc",
                    "entityBFamily": "Indicator",
                    "entityBType": "Account",
                    "fields": {},
                }
            ],
        ),
        (
            "malware_families",
            {"type": "type non support", "value": "1234567890"},
            {"malware_families": {"relations": "Test indicator"}},
            [
                {
                    "name": "type non support",
                    "reverseName": "related-to",
                    "type": "IndicatorToIndicator",
                    "entityA": "1234567890",
                    "entityAFamily": "Indicator",
                    "entityAType": "type non support",
                    "entityB": "relations",
                    "entityBFamily": "Indicator",
                    "entityBType": "Malware",
                    "fields": {},
                }
            ],
        ),
    ],
)
def test_create_relationships_unknown_key(field, indicator, resource, expected_results):
    """
    Given
        - Field type, indicator and a resource with an unknown relation type.
    When
        - Calling `create_relationships` command.
    Then
        - validate that no Key Error exception was thrown, and that only 1 relationship was created.
    """
    from CrowdStrikeIndicatorFeed import Client, create_relationships

    rs_ls = create_relationships(field, indicator, resource, Client.get_actors_names_request)
    assert rs_ls == expected_results
    assert len(rs_ls) == 1


def test_reset_last_run(mocker):
    """
    Given
        - No inputs.
    When
        - Calling `reset_last_run` command.
    Then
        - Ensure that the integration context dict was cleared.
    """
    from CrowdStrikeIndicatorFeed import reset_last_run

    demisto_set_context_mocker = mocker.patch.object(demisto, "setIntegrationContext")
    reset_last_run()
    assert demisto_set_context_mocker.call_args.args == ({},)


def test_fetch_no_indicators(mocker, requests_mock):
    """
    Given
        - no indicators api response
    When
        - fetching indicators
    Then
        - Ensure empty list is returned and no exception is raised.
    """
    from CrowdStrikeIndicatorFeed import Client

    mock_response = util_load_json("test_data/crowdstrike_indicators_list_command.json")
    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    requests_mock.get(url="https://api.crowdstrike.com/intel/combined/indicators/v1", json=mock_response)

    feed_tags = ["Tag1", "Tag2"]
    client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="Domain",
        include_deleted="false",
        limit=2,
        feed_tags=feed_tags,
    )

    mocker.patch.object(client, "get_indicators", return_value={"resources": []})

    assert client.fetch_indicators(limit=10, offset=5, fetch_command=True) == []


def test_crowdstrike_to_xsoar_types():
    from CrowdStrikeIndicatorFeed import CROWDSTRIKE_TO_XSOAR_TYPES

    assert None not in CROWDSTRIKE_TO_XSOAR_TYPES


@pytest.mark.parametrize(
    "first_fetch, filter, integration_context, get_indicators_response, filter_arg_call, expected_results",
    [
        ("1662650320", "", {"last_updated": "1662650343"}, {}, "(last_updated:>=1662650343)", ("", 0)),
        (
            "1662650320",
            "",
            {"last_updated": "1662650343"},
            {
                "resources": [
                    {
                        "id": "dummy",
                        "indicator": "dummy",
                        "type": "hash_md5",
                        "deleted": "False",
                        "published_date": 1622198010,
                        "last_updated": 1662650343,
                        "reports": [],
                        "actors": ["DOPPELSPIDER"],
                        "malware_families": ["DoppelDridex"],
                        "kill_chains": [],
                        "ip_address_types": [],
                        "domain_types": [],
                        "malicious_confidence": "high",
                        "_marker": "test_marker_test",
                        "labels": [],
                    }
                ]
            },
            "(last_updated:>=1662650343)",
            ("(_marker:>'test_marker_test')", 1),
        ),
        (
            "1662650320",
            "",
            {},
            {
                "resources": [
                    {
                        "id": "dummy",
                        "indicator": "dummy",
                        "type": "hash_md5",
                        "deleted": "False",
                        "published_date": 1622198010,
                        "last_updated": 1662650343,
                        "reports": [],
                        "actors": ["DOPPELSPIDER"],
                        "malware_families": ["DoppelDridex"],
                        "kill_chains": [],
                        "ip_address_types": [],
                        "domain_types": [],
                        "malicious_confidence": "high",
                        "_marker": "test_marker_test",
                        "labels": [],
                    }
                ]
            },
            "(last_updated:>=1662650320)",
            ("(_marker:>'test_marker_test')", 1),
        ),
        (
            "",
            "",
            {},
            {
                "resources": [
                    {
                        "id": "dummy",
                        "indicator": "dummy",
                        "type": "hash_md5",
                        "deleted": "False",
                        "published_date": 1622198010,
                        "last_updated": 1662650343,
                        "reports": [],
                        "actors": ["DOPPELSPIDER"],
                        "malware_families": ["DoppelDridex"],
                        "kill_chains": [],
                        "ip_address_types": [],
                        "domain_types": [],
                        "malicious_confidence": "high",
                        "_marker": "test_marker_test",
                        "labels": [],
                    }
                ]
            },
            None,
            ("(_marker:>'test_marker_test')", 1),
        ),
    ],
)
def test_handling_first_fetch_and_old_integration_context(
    mocker, requests_mock, first_fetch, filter, integration_context, get_indicators_response, filter_arg_call, expected_results
):
    from CrowdStrikeIndicatorFeed import Client

    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="ALL",
        include_deleted="false",
        limit=2,
        first_fetch=first_fetch,
    )
    mocker.patch("CrowdStrikeIndicatorFeed.demisto.getIntegrationContext", return_value=integration_context)
    requests_mock.get(
        url="https://api.crowdstrike.com/intel/entities/actors/v1?ids=DOPPELSPIDER&fields=name",
        json={"resources": [{"name": "DOPPEL SPIDER"}]},
    )
    get_indicator_call = mocker.patch.object(client, "get_indicators", return_value=get_indicators_response)

    results = client.handle_first_fetch_context_or_pre_2_1_0(filter)

    assert get_indicator_call.call_args.kwargs["params"].get("filter") == filter_arg_call
    assert results[0] == expected_results[0]
    assert len(results[1]) == expected_results[1]


@pytest.mark.parametrize(
    "indicator, expected_results",
    [
        ({"indicator": "1.1.1.1", "type": "ip_address"}, "IP"),
        ({"indicator": "fe80:0000:0000:0000:91ba:7558:26d3:acde", "type": "ip_address"}, "IPv6"),
        ({"indicator": "test_test", "type": "username"}, "Account"),
        ({"indicator": "test_test", "type": "password"}, None),
    ],
)
def test_auto_detect_indicator_type_from_cs(indicator: dict, expected_results: str | None):
    from CrowdStrikeIndicatorFeed import auto_detect_indicator_type_from_cs

    assert auto_detect_indicator_type_from_cs(indicator["indicator"], indicator["type"]) == expected_results


def test_get_actors_names_request_check_output(mocker, requests_mock):
    """
    Given
        - params for get_actors_names_request http request
    When
        - calling get_actors_names_request after fetching/listing indicators
    Then
        - Ensure the result is correct
    """
    from CrowdStrikeIndicatorFeed import Client

    mock_response = util_load_json("test_data/crowdstrike_indicators_list_command.json")
    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    requests_mock.get(url="https://api.crowdstrike.com/intel/combined/indicators/v1", json=mock_response)
    crowdstrike_client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="Domain",
        include_deleted="false",
        limit=2,
    )
    requests_mock.get(url="https://api.crowdstrike.com/intel/entities/actors/v1?", json={"resources": ""})
    requests_mock.get(
        url="https://api.crowdstrike.com/intel/entities/actors/v1?ids=123&fields=name", json={"resources": {"name": "TEST TEST"}}
    )
    res = crowdstrike_client.get_actors_names_request(params_string="ids=123&fields=name")
    assert res == {"name": "TEST TEST"}


def test_get_actors_names_request_called_with(mocker, requests_mock):
    """
    Given
        - params for get_actors_names_request http request
    When
        - calling get_actors_names_request after fetching/listing indicators
    Then
        - Ensure the request is called with the right args
    """
    from CrowdStrikeIndicatorFeed import Client

    mock_response = util_load_json("test_data/crowdstrike_indicators_list_command.json")
    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    requests_mock.get(url="https://api.crowdstrike.com/intel/combined/indicators/v1", json=mock_response)
    crowdstrike_client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="Domain",
        include_deleted="false",
        limit=2,
    )
    requests_mock.get(url="https://api.crowdstrike.com/intel/entities/actors/v1?", json={"resources": ""})
    http_request_mock = mocker.patch.object(
        crowdstrike_client, "_http_request", return_value={"resources": {"name": "TEST TEST"}}
    )
    crowdstrike_client.get_actors_names_request(params_string="ids=123&fields=name")
    http_request_mock.assert_called_once_with(
        method="GET", url_suffix="intel/entities/actors/v1?ids=123&fields=name", timeout=30, ok_codes=(200, 401)
    )


def test_crowdstrike_indicators_list_command_check_actors_convert(mocker, requests_mock):
    """
    Given
        - params for crowdstrike_indicators_list_command http request
    When
        - calling get_actors_names_request after fetching/listing indicators
    Then
        - Ensure the response is correct
    """
    from CrowdStrikeIndicatorFeed import Client, crowdstrike_indicators_list_command

    mock_response = util_load_json("test_data/crowdstrike_test_actors_convert.json")
    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    requests_mock.get(url="https://api.crowdstrike.com/intel/combined/indicators/v1", json=mock_response)
    requests_mock.get(
        url="https://api.crowdstrike.com/intel/entities/actors/v1?ids=TESTTEST&fields=name",
        json={"resources": [{"name": "TEST TEST"}]},
    )
    mocker.patch("CrowdStrikeIndicatorFeed.get_integration_context", return_value={})
    mocker.patch("CrowdStrikeIndicatorFeed.update_integration_context")
    feed_tags = ["Tag1", "Tag2"]
    crowdstrike_client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="Domain",
        include_deleted="false",
        limit=2,
        feed_tags=feed_tags,
    )
    args = {"limit": "2"}
    response = crowdstrike_indicators_list_command(crowdstrike_client, args)
    assert len(response.outputs) == 1
    assert len(response.raw_response) == 1
    assert len(response.raw_response[0].get("relationships", None)) == 2
    assert response.raw_response[0].get("relationships", None)[1].get("entityB", None) == "TEST TEST"


def test_change_actors_from_id_to_name(mocker, requests_mock):
    """
    Given
        - params for change_actors_from_id_to_name http request
    When
        - calling get_actors_names_request after fetching/listing indicators
    Then
        - Ensure the response is correct
    """
    from CrowdStrikeIndicatorFeed import Client, change_actors_from_id_to_name

    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    crowdstrike_client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="Domain",
        include_deleted="false",
        limit=2,
    )
    actors_unparsed_array = ["TEST", "TEST1", "TEST2"]
    mocker.patch("CrowdStrikeIndicatorFeed.get_integration_context", return_value={"TEST": "WAS IN CONTEXT"})
    requests_mock.get(
        url="https://api.crowdstrike.com/intel/entities/actors/v1?ids=TEST1&ids=TEST2&fields=name",
        json={"resources": [{"name": "Changedtest1"}, {"name": "Changedtest2"}]},
    )
    result = change_actors_from_id_to_name(actors_unparsed_array, crowdstrike_client.get_actors_names_request)
    assert result[0] == "WAS IN CONTEXT"
    assert result[1] == "Changedtest1"
    assert result[2] == "Changedtest2"


# This function will alternate the response each time it's called
def mock_alternate_response(request, context):
    # Track the number of times the post request has been called
    if not hasattr(mock_alternate_response, "call_count"):
        mock_alternate_response.call_count = 0
    mock_alternate_response.call_count += 1

    if mock_alternate_response.call_count == 1:
        # First call: Return 401 Unauthorized
        context.status_code = 401
        return {"errors": [{"code": 401, "message": "access denied, authorization failed"}]}
    else:
        # Second call: Return 200 OK with the access token
        context.status_code = 200
        return {"resources": []}


def test_fetch_no_indicators_regenerate_token(requests_mock):
    """
    Given
        - no indicators api response
    When
        - fetching indicators when API token expired.
    Then
        - Ensure empty list is returned and no exception is raised.
    """
    from CrowdStrikeIndicatorFeed import Client

    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    requests_mock.get(url="https://api.crowdstrike.com/intel/combined/indicators/v1", json=mock_alternate_response)

    feed_tags = ["Tag1", "Tag2"]
    client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="Domain",
        include_deleted="false",
        limit=2,
        feed_tags=feed_tags,
    )

    assert client.fetch_indicators(limit=10, offset=5, fetch_command=True) == []


def test_get_actors_names_request_regenerate_token(requests_mock):
    """
    Given
        - no actors api response
    When
        - calling get_actors_names_request after fetching/listing indicators
    Then
        - Ensure empty list is returned and no exception is raised.
    """
    from CrowdStrikeIndicatorFeed import Client

    mock_response = util_load_json("test_data/crowdstrike_indicators_list_command.json")
    requests_mock.post("https://api.crowdstrike.com/oauth2/token", json={"access_token": "12345"})
    requests_mock.get(url="https://api.crowdstrike.com/intel/combined/indicators/v1", json=mock_response)
    crowdstrike_client = Client(
        base_url="https://api.crowdstrike.com/",
        credentials={"identifier": "123", "password": "123"},
        type="Domain",
        include_deleted="false",
        limit=2,
    )
    requests_mock.get(url="https://api.crowdstrike.com/intel/entities/actors/v1?", json={"resources": ""})
    requests_mock.get(
        url="https://api.crowdstrike.com/intel/entities/actors/v1?ids=123&fields=name", json=mock_alternate_response
    )
    res = crowdstrike_client.get_actors_names_request(params_string="ids=123&fields=name")
    assert res == []