IgniteFeedV2
Flashpoint Ignite Feed V2 Integration allows importing indicators of compromise using the V2 API that provides a more concise, context-rich response structure. It includes sightings of IOCs over time and IOC relationships, providing visibility into an IOC's evolution. The indicators of compromise are ingested into Cortex XSOAR and displayed in the War Room.
Data Enrichment & Threat Intelligence · Flashpoint Feed · Feed
Details
| ID | IgniteFeedV2 |
|---|---|
| Provider | Flashpoint |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.10.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Flashpoint Ignite Feed V2 Integration allows importing indicators of compromise using the V2 API that provides a more concise, context-rich response structure. It includes sightings of IOCs over time and IOC relationships, providing visibility into an IOC’s evolution. The indicators of compromise are ingested into Cortex XSOAR and displayed in the War Room.
This integration was integrated and tested with API v2 of Flashpoint Ignite.
Fetch Indicators
Fetching the Ignite indicators. The indicators that are created or updated after the provided “First fetch time” will be fetched in the ascending order.
If you are upgrading from a Flashpoint Feed integration, please refer to the Migration Guide for guidance.
Configure Flashpoint Ignite Feed v2 in Cortex
- Navigate to Settings > Integrations > Servers & Services.
- Search for Flashpoint Ignite Feed v2.
-
Click Add instance to create and configure a new integration instance.
Parameter Description Required Server URL Server URL to connect to Ignite. True API Key API key used for secure communication with the Ignite platform. True Types of the indicators to fetch Types of the indicators to fetch. If not specified, it fetches all the indicators. Options: IPv4, IPv6, Domain, URL, File, Extracted Config. False CIDR Range of an IPv4 or IPv6 indicator CIDR range to filter IPv4 or IPv6 indicators.
Note: This parameter is applied only if the “Types of the indicators to fetch” is IPv4 or IPv6.False Maximum Severity Level of an indicator Filter indicators by their maximum severity level. If not specified, it fetches all the indicators. Options: Informational, Suspicious, Malicious. False Minimum Severity Level of an indicator Filter indicators by their minimum severity level. If not specified, it fetches all the indicators. Options: Informational, Suspicious, Malicious. False MITRE ATTACK IDs of an indicator Filter indicators by their MITRE ATTACK IDs.
Example: T1003, T1004.False Tags of an indicator Filter indicators by their tags. Must be exact tag matches.
Example: malware:cobaltstrike, actor:apt.False Actor Tags of an indicator Filter indicators by their actor tags. Must be exact tag matches. Inclusion of the actor: prefix is optional.
Example: actor:apt37, apt1.False Malware Tags of an indicator Filter indicators by their malware tags. Must be exact tag matches. Inclusion of the malware: prefix is optional.
Example: malware:cobaltstrike, amadey.False Source Tags of an indicator Filter indicators by their source tags. Must be exact tag matches. Inclusion of the source: prefix is optional.
Example: source:flashpoint_extraction, flashpoint_detection.False First fetch time Backfill indicators by providing date or relative timestamp. Default is ‘3 days’.
Formats accepted: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ, etc.False Fetch indicators Enable to fetch indicators. 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 Default Indicator Mapping When selected, all the incoming indicators will map to the Ignite Indicator. False Tags Provides the tags to be added to the indicators. Supports CSV values. False feedIncremental To indicate to the Cortex XSOAR server that a feed is incremental. Generally feeds that fetch based on a time range. For example, a daily feed which provides new indicators for the last day or a feed which is immutable and provides indicators from a search date onwards. False feedExpirationPolicy False feedExpirationInterval False Feed Fetch Interval Interval in minutes to fetch indicators. 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 Create relationships Create relationships between indicators as part of Enrichment. False Trust any certificate (not secure) Indicates whether to allow connections without verifying SSL certificate’s validity. False Use system proxy settings Indicates whether to use XSOAR’s system proxy settings to connect to the API. False - Click Test to validate the URLs, token, and connection.
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.
flashpoint-ignite-v2-get-indicators
Retrieves indicators from the Ignite V2 API. It displays the content of the fetch-indicators command.
Base Command
flashpoint-ignite-v2-get-indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum number of result objects to return. Maximum allowed limit is 500. Default is 10. | Optional |
| updated_since | Only retrieve values after the given timestamp. This parameter operates on the timestamp when an IOC was last modified. Formats accepted: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ, etc. Default is 3 days. |
Optional |
| types | Search by Indicator types. Supports comma separated values. Possible values are: IPv4, IPv6, Domain, URL, File, Extracted Config. | Optional |
| from | Fetch indicators after the given count of indicators. Default is 0. | Optional |
| cidr_range | CIDR range to filter IPv4 or IPv6 indicators. Note: This parameter is applied only if the “types” is IPv4 or IPv6. |
Optional |
| max_severity_level | Filter indicators by their maximum severity level. If not specified, it fetches all the indicators. Possible values are: Informational, Suspicious, Malicious | Optional |
| min_severity_level | Filter indicators by their minimum severity level. If not specified, it fetches all the indicators. Possible values are: Informational, Suspicious, Malicious | Optional |
| mitre_attack_ids | Filter indicators by their MITRE ATTACK IDs. Supports comma-separated values. | Optional |
| tags | Filter indicators by their tags. Must be exact tag matches. Supports comma-separated values. | Optional |
| actor_tags | Filter indicators by their actor tags. Must be exact tag matches. Inclusion of the actor: prefix is optional. Supports comma-separated values. | Optional |
| malware_tags | Filter indicators by their malware tags. Must be exact tag matches. Inclusion of the malware: prefix is optional. Supports comma-separated values. | Optional |
| source_tags | Filter indicators by their source tags. Must be exact tag matches. Inclusion of the source: prefix is optional. Supports comma-separated values. | Optional |
Context Output
There is no context output for this command.
Command example
!flashpoint-ignite-v2-get-indicators limit=2 types=URL updated_since="3 days"
Human Readable Output
Indicator(s)
ID Indicator Type Indicator Value Score Modified At Created At Last Seen At APT Description MITRE Attack IDs Sightings External References Total Sightings dummy-id-1 ipv4 0.0.0.1 malicious 2026-01-01T00:00:00Z 2026-01-01T00:00:00Z 2026-01-01T00:00:00Z N/A - id: T0001
name: Dummy Technique
tactics:
values: Defense-Evasion
tactic: Defense-Evasion
- id: T0002
name: Dummy Discovery
tactics:
values: Discovery
tactic: Discovery- id: dummy-sighting-1
href: https://api.example.com/sightings/dummy-sighting-1
source: dummy_source
sighted_at: 2026-01-01T00:00:00Z
tags:
values: malware:dummy-malware, source:dummy_source
related_iocs:
- id: dummy-related-id-1
type: file
value: dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
href: https://api.example.com/indicators/dummy-related-id-1
mitre_attack_ids:
- id: T0001
name: Dummy Technique
tactics:
values: Defense-Evasion
tactic: Defense-Evasion
- id: T0002
name: Dummy Discovery
tactics:
values: Discovery
tactic: Discovery
apt_description: N/A
malware_description: N/A
description: Observation: dummy-malware [2026-01-01T00:00:00Z]- source_name: Dummy Source
url: https://dummy.example.com1 dummy-id-2 file eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee unknown 2026-01-01T00:00:00Z 2026-01-01T00:00:00Z 2026-01-01T00:00:00Z - id: dummy-sighting-2
href: https://api.example.com/sightings/dummy-sighting-2
source: dummy_source
sighted_at: 2026-01-01T00:00:00Z
tags:
values: malware:dummy-malware-2, source:dummy_source
description: Observation: dummy-malware-2 [2026-01-01T00:00:00Z]1
Migration Guide
Migrated Commands
Some of the previous integration’s commands have been migrated to new commands. Below is the table showing the commands that have been migrated to the new ones.
| Flashpoint Ignite Command | Flashpoint Ignite v2 Command |
|---|---|
| flashpoint-ignite-get-indicators | flashpoint-ignite-v2-get-indicators |
Configuration parameters
url— Server URL (required)credentials— (required)types— Types of the indicators to fetchcidr_range— CIDR Range of an IPv4 or IPv6 indicatormax_score— Maximum Severity Level of an indicatormin_score— Minimum Severity Level of an indicatormitre_attack_ids— MITRE ATTACK IDs of an indicatortags— Tags of an indicatoractor_tags— Actor Tags of an indicatormalware_tags— Malware Tags of an indicatorsource_tags— Source Tags of an indicatorfirst_fetch— First fetch timefeed— Fetch indicatorsfeedReputation— Indicator ReputationfeedReliability— Source Reliability (required)tlp_color— Traffic Light Protocol ColordefaultMap— Default Indicator MappingfeedTags— TagsfeedIncremental—feedExpirationPolicy—feedExpirationInterval—feedFetchInterval— Feed Fetch IntervalfeedBypassExclusionList— Bypass exclusion listcreateRelationship— Create relationshipsinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (1)
-
flashpoint-ignite-v2-get-indicatorsRetrieves indicators from the Ignite V2 API. It displays the content of the fetch-indicators command.
"""Flashpoint Ignite Feed V2 Integration for Cortex XSOAR - Unit Tests file""" import json import os import sys import pytest from requests.exceptions import HTTPError import IgniteFeedV2 from CommonServerPython import DemistoException from IgniteFeedV2 import ( HTTP_ERRORS, MAX_FETCH, MESSAGES, URL_SUFFIX, Client, demisto, fetch_indicators_command, flashpoint_ignite_v2_get_indicators_command, main, convert_types_to_ioc_types, validate_get_indicators_args, validate_fetch_indicators_params, prepare_hr_for_indicators, remove_space_from_args, validate_score_params, ) from IgniteFeedV2 import test_module as main_test_module """ CONSTANTS """ API_KEY = "dummy_api_key" MOCK_URL = "https://mock_dummy.com" """ UTILITY FUNCTIONS AND FIXTURES """ def util_load_json(path): """ Load json file into dictionary. :param path: Takes file path. :return: Dictionary. """ with open(path, encoding="utf-8") as f: return json.loads(f.read()) @pytest.fixture def mock_client(): """Mock a client object with required data to mock.""" client = Client(url=MOCK_URL, headers={}, proxy=False, verify=False) return client class MockResponse: """Creates mock response.""" def __init__(self, status_code, json_data=None, headers=None): """Initialize class object.""" self.status_code = status_code self._json_data = json_data or {} self.headers = headers or {} def json(self): """Return json data.""" return self._json_data def raise_for_status(self): """Raise status code error.""" if self.status_code != 200: raise HTTPError("test") """ TEST CASES """ class TestTestModule: """Test cases for test_module function.""" def test_test_module_with_invalid_apikey(self, requests_mock, mock_client): """ Test case scenario for the execution of test_module with invalid apikey. Given: - mocked client with invalid apikey. When: - Calling `test_module` function. Then: - Returns exception. """ indicator_list_response_401: str = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/invalid_apikey_401.json") ) requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json=indicator_list_response_401, status_code=401) with pytest.raises(DemistoException) as err: main_test_module(client=mock_client) assert str(err.value) == HTTP_ERRORS[401] def test_test_module_with_valid_apikey(self, requests_mock, mock_client): """ Test case scenario for successful execution of test_module. Given: - mocked client with valid apikey. When: - Calling `test_module` function. Then: - Returns an ok message. """ indicator_list_response_200: dict = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/indicator_list_v2_200.json") ) requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json=indicator_list_response_200, status_code=200) assert main_test_module(client=mock_client) == "ok" @pytest.mark.parametrize( "params, err_msg", [ ( {"url": "", "credentials": {"password": API_KEY}, "integrationReliability": "B - Usually reliable"}, MESSAGES["NO_PARAM_PROVIDED"].format("Server URL"), ), ( {"url": MOCK_URL, "credentials": "", "integrationReliability": "B - Usually reliable"}, MESSAGES["NO_PARAM_PROVIDED"].format("API Key"), ), ], ) def test_test_module_when_invalid_params_provided(self, params, err_msg, mocker, capfd): """ Test case scenario for execution of test_module when invalid argument provided. Given: - Params for test_module. When: - Calling `main` function. Then: - Returns exception. """ mocker.patch.object(demisto, "params", return_value=params) mocker.patch.object(demisto, "command", return_value="test-module") mocker.patch.object(sys, "exit", return_value=None) return_error = mocker.patch.object(IgniteFeedV2, "return_error") capfd.close() main() assert err_msg in return_error.call_args[0][0] def test_test_module_with_isfetch(self, requests_mock, mocker, mock_client): """ Test case scenario for successful execution of test_module with feed enabled. Given: - mocked client with valid apikey and feed enabled. When: - Calling `test_module` function. Then: - Returns an ok message. """ requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json={"items": []}, status_code=200) mocker.patch.object(demisto, "params", return_value={"feed": True, "url": MOCK_URL, "credentials": {"password": API_KEY}}) assert main_test_module(mock_client) == "ok" class TestHttpRequest: """Test cases for http_request method.""" @pytest.mark.parametrize("status_code", [400, 401, 403, 404, 500]) def test_http_request_when_error_is_returned(self, requests_mock, mock_client, status_code): """ Tests http_request method of Client class. Given: - Status codes of requests. When: - Calling `http_request` function. Then: - Returns exception. """ requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), status_code=status_code) with pytest.raises(DemistoException) as e: mock_client.http_request(method="GET", url_suffix=URL_SUFFIX["INDICATORS"], params={}) assert str(e.value) == HTTP_ERRORS[status_code] def test_http_request_when_raise_for_status(self, mock_client): """Tests http_request when error raised for status.""" resp = MockResponse(status_code=503) with pytest.raises(HTTPError): mock_client.handle_errors(resp) def test_http_request_when_422_validation_error(self, mock_client): """ Tests handle_errors method when 422 validation error is returned. Given: - 422 status code with validation error response. When: - Calling `handle_errors` function. Then: - Returns meaningful validation error message. """ validation_error_response = { "detail": "Request Validation Error", "errors": [ { "type": "ip_any_network", "loc": ["query", "cidr_range"], "msg": "value is not a valid IPv4 or IPv6 network", "input": "0.0.0.1/24", } ], } resp = MockResponse(status_code=422, json_data=validation_error_response) with pytest.raises(DemistoException) as e: mock_client.handle_errors(resp) assert "Validation Error" in str(e.value) assert "cidr_range" in str(e.value) assert "value is not a valid IPv4 or IPv6 network" in str(e.value) assert "0.0.0.1/24" in str(e.value) def test_parse_validation_error_multiple_errors(self, mock_client): """ Tests parse_validation_error with multiple validation errors. Given: - Response with multiple validation errors. When: - Calling `parse_validation_error` method. Then: - Returns formatted error message with all errors. """ class MockResp: def json(self): return { "detail": "Request Validation Error", "errors": [ {"loc": ["query", "min_score"], "msg": "invalid value", "input": "bad"}, {"loc": ["query", "max_score"], "msg": "invalid value", "input": "worse"}, ], } result = mock_client.parse_validation_error(MockResp()) assert "Validation Error" in result assert "min_score" in result assert "max_score" in result class TestGetIndicatorsCommand: """Test cases for flashpoint_ignite_v2_get_indicators_command.""" def test_get_indicators_command_when_invalid_argument_provided(self, mock_client): """ Test case scenario for execution of flashpoint-ignite-get-indicators command when invalid argument provided. Given: - command arguments for flashpoint_ignite_v2_get_indicators_command. When: - Calling `flashpoint_ignite_v2_get_indicators_command` function. Then: - Returns a valid error message. """ with pytest.raises(ValueError) as err: flashpoint_ignite_v2_get_indicators_command(client=mock_client, params={}, args={"limit": -1}) assert str(err.value) == MESSAGES["LIMIT_ERROR"].format(-1, MAX_FETCH) def test_get_indicators_command_when_limit_exceeds_max(self, mock_client): """ Test case scenario when limit exceeds maximum allowed. Given: - command arguments with limit exceeding MAX_FETCH. When: - Calling `flashpoint_ignite_v2_get_indicators_command` function. Then: - Returns a valid error message. """ with pytest.raises(ValueError) as err: flashpoint_ignite_v2_get_indicators_command(client=mock_client, params={}, args={"limit": MAX_FETCH + 1}) assert str(err.value) == MESSAGES["LIMIT_ERROR"].format(MAX_FETCH + 1, MAX_FETCH) def test_get_indicators_command_when_valid_response_is_returned(self, requests_mock, mock_client): """ Test case scenario for execution of flashpoint-ignite-get-indicators command when valid response is returned. Given: - command arguments for flashpoint_ignite_v2_get_indicators_command. When: - Calling `flashpoint_ignite_v2_get_indicators_command` function. Then: - Returns a valid output. """ args = {"limit": 2} indicator_list_response_200: dict = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/indicator_list_v2_200.json") ) requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json=indicator_list_response_200, status_code=200) with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./test_data/indicator_list_v2_hr.md")) as file: hr_output = file.read() indicators: dict = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/indicators_v2.json") ) actual = flashpoint_ignite_v2_get_indicators_command(client=mock_client, params={"tlp_color": "AMBER"}, args=args) assert actual.raw_response == indicators assert actual.readable_output == hr_output def test_get_indicators_command_when_create_relationship_is_true(self, requests_mock, mock_client): """ Test case scenario for execution of flashpoint-ignite-get-indicators command when create relationship is true. Given: - command arguments for flashpoint_ignite_v2_get_indicators_command. When: - Calling `flashpoint_ignite_v2_get_indicators_command` function. Then: - Returns a valid output with relationships. """ args = {"limit": 2} indicator_list_response_200: dict = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/indicator_list_v2_200.json") ) requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json=indicator_list_response_200, status_code=200) with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./test_data/indicator_list_v2_hr.md")) as file: hr_output = file.read() indicators: dict = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/indicators_v2_with_relationship.json") ) actual = flashpoint_ignite_v2_get_indicators_command(client=mock_client, params={"createRelationship": True}, args=args) assert actual.raw_response == indicators assert actual.readable_output == hr_output def test_get_indicators_command_when_empty_response_is_returned(self, requests_mock, mock_client): """ Test case scenario for execution of flashpoint-ignite-get-indicators command when empty response is returned. Given: - command arguments for flashpoint_ignite_v2_get_indicators_command. When: - Calling `flashpoint_ignite_v2_get_indicators_command` function. Then: - Returns a no indicator message. """ args = {"limit": 2} requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json={"items": []}, status_code=200) actual = flashpoint_ignite_v2_get_indicators_command(client=mock_client, params={}, args=args) assert actual.readable_output == MESSAGES["NO_INDICATORS_FOUND"] class TestFetchIndicatorsCommand: """Test cases for fetch_indicators_command.""" def test_fetch_indicators_command_when_valid_response_is_returned(self, requests_mock, mock_client): """ Test case scenario for execution of fetch-indicators command when valid response is returned. Given: - command arguments for fetch_indicators_command. When: - Calling `fetch_indicators_command` function. Then: - Returns a valid output. """ indicator_list_response_200: dict = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/indicator_list_v2_200.json") ) requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json=indicator_list_response_200, status_code=200) indicators: dict = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/indicators_v2.json") ) result = fetch_indicators_command(client=mock_client, params={"tlp_color": "AMBER"}, last_run={}) assert result[0] == indicators def test_fetch_indicators_command_pagination(self, requests_mock, mock_client): """ Test case scenario for fetch-indicators command pagination. Given: - Previous last_run with offset. When: - Calling `fetch_indicators_command` function. Then: - Returns correct next_run with updated offset. """ indicator_list_response_200: dict = util_load_json( os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/indicator_list_v2_200.json") ) requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json=indicator_list_response_200, status_code=200) last_run = {"next_modified_after": "2024-04-01T00:00:00Z", "next_modified_before": "2024-04-15T00:00:00Z", "offset": 0} _, next_run = fetch_indicators_command(client=mock_client, params={}, last_run=last_run) # Since we got less than MAX_FETCH, next_run should update modified_after assert "next_modified_after" in next_run def test_fetch_indicators_command_empty_response(self, requests_mock, mock_client): """ Test case scenario for fetch-indicators command with empty response. Given: - Empty response from API. When: - Calling `fetch_indicators_command` function. Then: - Returns empty indicators list. """ requests_mock.get(os.path.join(MOCK_URL, URL_SUFFIX["INDICATORS"]), json={"items": []}, status_code=200) indicators, _ = fetch_indicators_command(client=mock_client, params={}, last_run={}) assert indicators == [] class TestHelperFunctions: """Test cases for helper functions.""" def test_convert_types_to_ioc_types(self): """ Test case for convert_types_to_ioc_types function. Given: - List of indicator types. When: - Calling `convert_types_to_ioc_types` function. Then: - Returns correct ioc_types string. """ types_list = ["IPv4", "IPv6", "Domain", "URL"] result = convert_types_to_ioc_types(types_list) assert "ipv4" in result assert "ipv6" in result assert "domain" in result assert "url" in result def test_convert_types_to_ioc_types_empty(self): """ Test case for convert_types_to_ioc_types with empty list. Given: - Empty list. When: - Calling `convert_types_to_ioc_types` function. Then: - Returns empty string. """ result = convert_types_to_ioc_types([]) assert result == "" def test_validate_get_indicators_args(self): """ Test case for validate_get_indicators_args function. Given: - Valid arguments. When: - Calling `validate_get_indicators_args` function. Then: - Returns correctly formatted params for v2 API. """ args = {"limit": 10, "types": "IPv4,Domain", "updated_since": "3 days"} result = validate_get_indicators_args(args) assert result["size"] == 10 assert "ioc_types" in result assert "modified_after" in result assert result["sort"] == "modified_at:asc" assert result["embed"] == "all" def test_validate_fetch_indicators_params(self): """ Test case for validate_fetch_indicators_params function. Given: - Parameters and last_run. When: - Calling `validate_fetch_indicators_params` function. Then: - Returns correctly formatted params for v2 API. """ params = {"types": ["IPv4", "Domain"], "first_fetch": "3 days"} last_run: dict = {} result = validate_fetch_indicators_params(params, last_run) assert "size" in result assert "modified_after" in result assert "modified_before" in result assert result["sort"] == "modified_at:asc" assert result["embed"] == "all" def test_remove_space_from_args(self): """ Test case for remove_space_from_args function. Given: - Arguments with spaces. When: - Calling `remove_space_from_args` function. Then: - Returns arguments without leading/trailing spaces. """ args = {"key1": " value1 ", "key2": "value2", "key3": 123} result = remove_space_from_args(args) assert result["key1"] == "value1" assert result["key2"] == "value2" assert result["key3"] == 123 def test_validate_get_indicators_args_invalid_types(self): """ Test case for validate_get_indicators_args with invalid types. Given: - Arguments with invalid types. When: - Calling `validate_get_indicators_args` function. Then: - Raises ValueError with INVALID_TYPES message. """ args = {"limit": 10, "types": "InvalidType"} with pytest.raises(ValueError) as err: validate_get_indicators_args(args) assert MESSAGES["INVALID_TYPES"].format("['InvalidType']") in str(err.value) def test_validate_score_params_valid(self): """ Test case for validate_score_params with valid scores. Given: - Valid min_score and max_score values. When: - Calling `validate_score_params` function. Then: - Returns correct lowercase score values. """ min_val, max_val = validate_score_params("Suspicious", "Malicious") assert min_val == "suspicious" assert max_val == "malicious" def test_validate_score_params_invalid_score(self): """ Test case for validate_score_params with invalid score. Given: - Invalid score value. When: - Calling `validate_score_params` function. Then: - Raises ValueError with INVALID_SCORE message. """ with pytest.raises(ValueError) as err: validate_score_params("InvalidScore", None) assert MESSAGES["INVALID_SCORE"].format("InvalidScore") in str(err.value) def test_validate_score_params_invalid_range(self): """ Test case for validate_score_params with invalid range. Given: - min_score greater than max_score. When: - Calling `validate_score_params` function. Then: - Raises ValueError with INVALID_SCORE_RANGE message. """ with pytest.raises(ValueError) as err: validate_score_params("Malicious", "Informational") assert MESSAGES["INVALID_SCORE_RANGE"] in str(err.value) def test_validate_fetch_indicators_params_with_new_params(self): """ Test case for validate_fetch_indicators_params with new filter params. Given: - Parameters with new filter options. When: - Calling `validate_fetch_indicators_params` function. Then: - Returns params with all new filters applied. """ params = { "types": ["IPv4"], "first_fetch": "3 days", "cidr_range": "0.0.0.1/16", "min_score": "Suspicious", "max_score": "Malicious", "mitre_attack_ids": ["T1001", "T1002"], "tags": ["tag1", "tag2"], "actor_tags": ["actor1"], "malware_tags": ["malware1"], "source_tags": ["source1"], } last_run: dict = {} result = validate_fetch_indicators_params(params, last_run) assert result["size"] == 500 assert result["ioc_types"] == "ipv4" assert result["cidr_range"] == "0.0.0.1/16" assert result["min_score"] == "suspicious" assert result["max_score"] == "malicious" assert result["mitre_attack_ids"] == "T1001,T1002" assert result["tags"] == "tag1,tag2" assert result["actors"] == "actor1" assert result["malware"] == "malware1" assert result["sources"] == "source1" class TestClientMethods: """Test cases for Client class methods.""" def test_check_indicator_type_auto_detect(self, mock_client): """ Test case for check_indicator_type with auto detection. Given: - IP address indicator value. When: - Calling `check_indicator_type` with default_map=False. Then: - Returns detected IP type. """ result = mock_client.check_indicator_type("0.0.0.1", indicator_type="ipv4", default_map=False) assert result == "IP" def test_check_indicator_type_default_map(self, mock_client): """ Test case for check_indicator_type with default mapping. Given: - Any indicator value. When: - Calling `check_indicator_type` with default_map=True. Then: - Returns default Ignite Indicator type. """ result = mock_client.check_indicator_type("0.0.0.1", indicator_type="ipv4", default_map=True) assert result == "Ignite Indicator" def test_get_nested_value(self, mock_client): """ Test case for _get_nested_value method. Given: - Nested dictionary and path. When: - Calling `_get_nested_value` method. Then: - Returns correct nested value. """ data = {"level1": {"level2": {"value": "test_value"}}} result = mock_client._get_nested_value(data, "level1.level2.value") assert result == "test_value" def test_get_nested_value_not_found(self, mock_client): """ Test case for _get_nested_value with non-existent path. Given: - Dictionary and non-existent path. When: - Calling `_get_nested_value` method. Then: - Returns None. """ data = {"level1": {"level2": "value"}} result = mock_client._get_nested_value(data, "level1.level3.value") assert result is None def test_create_indicators_from_response(self, mock_client): """ Test case for create_indicators_from_response method. Given: - V2 API response items. When: - Calling `create_indicators_from_response` method. Then: - Returns correctly formatted indicators. """ api_responses = util_load_json(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/api_responses.json")) response = api_responses["ipv4"] params = {"feedTags": ["tag1"], "tlp_color": "AMBER"} indicators = mock_client.create_indicators_from_response(response, params) assert len(indicators) == 1 assert indicators[0]["value"] == "0.0.0.1" assert indicators[0]["type"] == "IP" assert indicators[0]["fields"]["trafficlightprotocol"] == "AMBER" assert indicators[0]["fields"]["flashpointfeedindicatorid"] == "dummy-id-1" assert indicators[0]["fields"]["flashpointfeedindicatortype"] == "ipv4" def test_create_indicators_from_response_file_type(self, mock_client): """ Test case for create_indicators_from_response with file type indicator. Given: - V2 API response with file type indicator. When: - Calling `create_indicators_from_response` method. Then: - Returns indicator with value from API response. """ api_responses = util_load_json(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/api_responses.json")) response = api_responses["file"] params: dict = {} indicators = mock_client.create_indicators_from_response(response, params) assert len(indicators) == 1 assert indicators[0]["value"] == "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" assert indicators[0]["fields"]["sha256"] == "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" def test_map_indicator_fields(self, mock_client): """ Test case for map_indicator_fields method. Given: - V2 API response item and indicator object. When: - Calling `map_indicator_fields` method. Then: - Correctly maps all fields. """ api_responses = util_load_json(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/api_responses.json")) resp = api_responses["map_indicator_fields"] indicator_obj: dict = {"fields": {"tags": []}} mock_client.map_indicator_fields(resp, indicator_obj) assert indicator_obj["fields"]["flashpointfeedindicatorid"] == "dummy-id-1" assert indicator_obj["fields"]["flashpointfeedindicatortype"] == "ipv4" assert indicator_obj["fields"]["flashpointfeedtotalsightings"] == 5 assert indicator_obj["fields"]["flashpointfeedcreateddate"] == "2026-01-01T00:00:00Z" assert indicator_obj["fields"]["flashpointfeedlastseendate"] == "2026-01-01T00:00:00Z" assert indicator_obj["fields"]["flashpointfeedapi"] == "https://api.example.com/indicators/dummy-id-1" assert indicator_obj["fields"]["flashpointfeedplatformurl"] == "https://app.example.com/iocs/dummy-id-1" assert indicator_obj["fields"]["flashpointfeedlastscoredate"] == "2026-01-01T00:00:00Z" assert indicator_obj["fields"]["flashpointfeedmodifieddate"] == "2026-01-01T00:00:00Z" class TestPrepareHrForIndicators: """Test cases for prepare_hr_for_indicators function.""" def test_prepare_hr_for_indicators(self): """ Test case for prepare_hr_for_indicators function. Given: - List of indicators. When: - Calling `prepare_hr_for_indicators` function. Then: - Returns correctly formatted markdown table. """ hr_data = util_load_json(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/hr_indicators.json")) indicators = hr_data["ipv4"] result = prepare_hr_for_indicators(indicators) assert "### Indicator(s)" in result assert "dummy-id-1" in result assert "0.0.0.1" in result assert "malicious" in result def test_prepare_hr_for_indicators_file_type(self): """ Test case for prepare_hr_for_indicators with file type. Given: - File type indicator with hashes. When: - Calling `prepare_hr_for_indicators` function. Then: - Returns File as indicator type and uses SHA256 hash. """ hr_data = util_load_json(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/hr_indicators.json")) indicators = hr_data["file"] result = prepare_hr_for_indicators(indicators) assert "file" in result assert "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" in result def test_prepare_hr_for_indicators_with_new_fields(self): """ Test case for prepare_hr_for_indicators with new fields. Given: - Indicator with APT description, malware description, MITRE IDs, etc. When: - Calling `prepare_hr_for_indicators` function. Then: - Returns markdown with all new fields. """ hr_data = util_load_json(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/hr_indicators.json")) indicators = hr_data["with_new_fields"] result = prepare_hr_for_indicators(indicators) assert "### Indicator(s)" in result assert "0.0.0.1" in result assert "malicious" in result class TestValidateGetIndicatorsArgsNewParams: """Test cases for validate_get_indicators_args with new parameters.""" def test_validate_get_indicators_args_with_from_parameter(self): """ Test case for validate_get_indicators_args with from parameter. Given: - Valid from parameter. When: - Calling `validate_get_indicators_args` function. Then: - Returns params with correct from value. """ args = {"limit": 10, "from": 100} result = validate_get_indicators_args(args) assert result["from"] == 100 assert result["size"] == 10 def test_validate_get_indicators_args_with_negative_from(self): """ Test case for validate_get_indicators_args with negative from. Given: - Negative from parameter. When: - Calling `validate_get_indicators_args` function. Then: - Raises ValueError with FROM_ERROR message. """ args = {"limit": 10, "from": -5} with pytest.raises(ValueError) as err: validate_get_indicators_args(args) assert MESSAGES["FROM_ERROR"].format(args.get("from")) in str(err.value) def test_validate_get_indicators_args_with_all_new_params(self): """ Test case for validate_get_indicators_args with all new filter params. Given: - Arguments with all new filter parameters. When: - Calling `validate_get_indicators_args` function. Then: - Returns params with all filters applied. """ args = { "limit": 50, "from": 0, "cidr_range": "0.0.0.1/8", "min_severity_level": "Informational", "max_severity_level": "Malicious", "mitre_attack_ids": "T1001,T1002", "tags": "tag1,tag2", "actor_tags": "actor1", "malware_tags": "malware1", "source_tags": "source1", } result = validate_get_indicators_args(args) assert result["size"] == 50 assert result["from"] == 0 assert result["cidr_range"] == "0.0.0.1/8" assert result["min_score"] == "informational" assert result["max_score"] == "malicious" assert result["mitre_attack_ids"] == "T1001,T1002" assert result["tags"] == "tag1,tag2" assert result["actors"] == "actor1" assert result["malware"] == "malware1" assert result["sources"] == "source1" assert result["embed"] == "all" assert result["sort"] == "modified_at:asc" def test_validate_get_indicators_args_without_optional_params(self): """ Test case for validate_get_indicators_args without optional params. Given: - Only required arguments (limit). When: - Calling `validate_get_indicators_args` function. Then: - Returns params without optional filter fields. """ args = {"limit": 10} result = validate_get_indicators_args(args) assert result["size"] == 10 assert result["from"] == 0 assert "cidr_range" not in result assert "min_score" not in result assert "max_score" not in result class TestParseValidationErrorEdgeCases: """Test cases for parse_validation_error edge cases.""" def test_parse_validation_error_no_errors_array(self, mock_client): """ Test parse_validation_error when errors array is empty. Given: - Response with empty errors array. When: - Calling `parse_validation_error` method. Then: - Returns error with detail message. """ class MockResp: def json(self): return {"detail": "Request Validation Error", "errors": []} result = mock_client.parse_validation_error(MockResp()) assert "Validation Error" in result def test_parse_validation_error_without_input(self, mock_client): """ Test parse_validation_error when input field is missing. Given: - Response with error missing input field. When: - Calling `parse_validation_error` method. Then: - Returns error message without input. """ class MockResp: def json(self): return {"detail": "Request Validation Error", "errors": [{"loc": ["query", "param"], "msg": "invalid"}]} result = mock_client.parse_validation_error(MockResp()) assert "param: invalid" in result assert "input:" not in result def test_parse_validation_error_json_exception(self, mock_client): """ Test parse_validation_error when json parsing fails. Given: - Response that raises exception on json(). When: - Calling `parse_validation_error` method. Then: - Returns default HTTP error message. """ class MockResp: def json(self): raise ValueError("Invalid JSON") result = mock_client.parse_validation_error(MockResp()) assert result == HTTP_ERRORS[422] class TestValidateScoreParamsEdgeCases: """Test cases for validate_score_params edge cases.""" def test_validate_score_params_only_min(self): """ Test validate_score_params with only min_score. Given: - Only min_score provided. When: - Calling `validate_score_params` function. Then: - Returns min_score and None for max_score. """ min_val, max_val = validate_score_params("Informational", None) assert min_val == "informational" assert max_val is None def test_validate_score_params_only_max(self): """ Test validate_score_params with only max_score. Given: - Only max_score provided. When: - Calling `validate_score_params` function. Then: - Returns None for min_score and max_score value. """ min_val, max_val = validate_score_params(None, "Suspicious") assert min_val is None assert max_val == "suspicious" def test_validate_score_params_both_none(self): """ Test validate_score_params with both None. Given: - Both min_score and max_score are None. When: - Calling `validate_score_params` function. Then: - Returns None for both. """ min_val, max_val = validate_score_params(None, None) assert min_val is None assert max_val is None def test_validate_score_params_same_value(self): """ Test validate_score_params with same min and max. Given: - Same value for min_score and max_score. When: - Calling `validate_score_params` function. Then: - Returns same value for both (no error). """ min_val, max_val = validate_score_params("Suspicious", "Suspicious") assert min_val == "suspicious" assert max_val == "suspicious" def test_validate_score_params_case_insensitive(self): """ Test validate_score_params is case insensitive. Given: - Score values in different cases. When: - Calling `validate_score_params` function. Then: - Returns lowercase values. """ min_val, max_val = validate_score_params("INFORMATIONAL", "MALICIOUS") assert min_val == "informational" assert max_val == "malicious"