HTTPFeedApiModule
Common HTTP feed code that will be appended into each HTTP feed integration when it's deployed.
python · ApiModules
Details
| ID | HTTPFeedApiModule |
|---|---|
| Language | python |
| From Version | 5.0.0 |
| Docker Image | demisto/py3-tools:1.0.0.108682 |
| Tags | infra server |
README
To use the common HTTP Feed API logic, run the following:
def main():
feed_main(<FEED_NAME>)
from HTTPFeedApiModule import * # noqa: E402
if __name__ in ["builtins", "__main__"]:
main()
feed_main is the main execution of the Feed API module. It can be extended or overriden in the integration main function.
Note that the module expectes a feed_types parameter to extract the indicator. This is similar to the configuration in minemeld.
See the module class docstring for an example.
import json from unittest.mock import patch from HTTPFeedApiModule import ( get_indicators_command, Client, datestring_to_server_format, feed_main, fetch_indicators_command, get_no_update_value, convert_cidr32_to_ip, is_cidr_32, ) import requests_mock import demistomock as demisto import pytest import requests def test_get_indicators(): with open("test_data/asn_ranges.txt") as asn_ranges_txt: asn_ranges = asn_ranges_txt.read().encode("utf8") with requests_mock.Mocker() as m: itype = "ASN" args = {"indicator_type": itype, "limit": 35} feed_type = { "https://www.spamhaus.org/drop/asndrop.txt": { "indicator_type": "ASN", "indicator": {"regex": "^AS[0-9]+"}, "fields": [ {"asndrop_country": {"regex": r"^.*;\W([a-zA-Z]+)\W+", "transform": r"\1"}}, {"asndrop_org": {"regex": r"^.*\|\W+(.*)", "transform": r"\1"}}, ], } } m.get("https://www.spamhaus.org/drop/asndrop.txt", content=asn_ranges) client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", source_name="spamhaus", ignore_regex="^;.*", feed_url_to_config=feed_type, ) args["indicator_type"] = "ASN" _, _, raw_json = get_indicators_command(client, args) for ind_json in raw_json: ind_val = ind_json.get("value") ind_type = ind_json.get("type") ind_rawjson = ind_json.get("rawJSON") assert ind_val assert ind_type == itype assert ind_rawjson["value"] == ind_val assert ind_rawjson["type"] == ind_type def test_get_indicators_json_params(): with open("test_data/asn_ranges.txt") as asn_ranges_txt: asn_ranges = asn_ranges_txt.read().encode("utf8") with requests_mock.Mocker() as m: itype = "ASN" args = {"indicator_type": itype, "limit": 35} indicator_json = """ { "regex": "^AS[0-9]+" } """ fields_json = r""" { "asndrop_country": { "regex":"^.*;\\W([a-zA-Z]+)\\W+", "transform":"\\1" }, "asndrop_org": { "regex":"^.*\\|\\W+(.*)", "transform":"\\1" } } """ m.get("https://www.spamhaus.org/drop/asndrop.txt", content=asn_ranges) client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", source_name="spamhaus", ignore_regex="^;.*", indicator=indicator_json, fields=fields_json, indicator_type="ASN", ) args["indicator_type"] = "ASN" _, _, raw_json = get_indicators_command(client, args) for ind_json in raw_json: ind_val = ind_json.get("value") ind_type = ind_json.get("type") ind_rawjson = ind_json.get("rawJSON") assert ind_val assert ind_type == itype assert ind_rawjson["value"] == ind_val assert ind_rawjson["type"] == ind_type def test_custom_fields_creator(): custom_fields_mapping = {"old_field1": "new_field1", "old_field2": "new_field2"} client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", feed_url_to_config="some_stuff", custom_fields_mapping=custom_fields_mapping, ) attributes = {"old_field1": "value1", "old_field2": "value2"} custom_fields = client.custom_fields_creator(attributes) assert custom_fields.get("new_field1") == "value1" assert custom_fields.get("new_field2") == "value2" assert "old_field1" not in custom_fields assert "old_filed2" not in custom_fields def test_datestring_to_server_format(): """ Given - A string represting a date. When - running datestring_to_server_format on the date. Then - Ensure the datestring is converted to the ISO-8601 format. """ datestring1 = "2020-02-10 13:39:14" datestring2 = "2020-02-10T13:39:14" datestring3 = "2020-02-10 13:39:14.123" datestring4 = "2020-02-10T13:39:14.123" datestring5 = "2020-02-10T13:39:14Z" datestring6 = "2020-11-01T04:16:13-04:00" assert datestring_to_server_format(datestring1) == "2020-02-10T13:39:14Z" assert datestring_to_server_format(datestring2) == "2020-02-10T13:39:14Z" assert datestring_to_server_format(datestring3) == "2020-02-10T13:39:14Z" assert datestring_to_server_format(datestring4) == "2020-02-10T13:39:14Z" assert datestring_to_server_format(datestring5) == "2020-02-10T13:39:14Z" assert datestring_to_server_format(datestring6) == "2020-11-01T08:16:13Z" def test_is_cidr_32(): """ Test function for is_cidr_32. """ test_cases = [ ("192.0.2.1/32", True), ("192.0.2.1/24", False), ("192.0.2.1/33", False), ("invalid", False), (123, False), ] for input_value, expected_output in test_cases: actual_output = is_cidr_32(input_value) assert actual_output == expected_output def test_convert_cidr32_to_ip(): """ Test function for convert_cidr32_to_ip. """ test_cases = [ ("192.0.2.1/32", "192.0.2.1"), ("192.0.2.1/24", None), ("192.0.2.1/abc", None), ] for input_value, expected_output in test_cases: actual_output = convert_cidr32_to_ip(input_value) assert actual_output == expected_output def test_get_feed_config(): custom_fields_mapping = {"old_field1": "new_field1", "old_field2": "new_field2"} client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", feed_url_to_config="some_stuff", custom_fields_mapping=custom_fields_mapping, ) # Check that if an empty .get_feed_config is called, an empty dict returned assert client.get_feed_config() == {} def test_feed_main_fetch_indicators(mocker, requests_mock): """ Given - Parameters (url, ignore_regex, feed_url_to_config and tags) to configure a feed. When - Fetching indicators. Then - Ensure createIndicators is called with 466 indicators to fetch. - Ensure one of the indicators is fetched as expected. """ feed_url = "https://www.spamhaus.org/drop/asndrop.txt" indicator_type = "ASN" tags = "tag1,tag2" tlp_color = "AMBER" feed_url_to_config = { "https://www.spamhaus.org/drop/asndrop.txt": { "indicator_type": indicator_type, "indicator": {"regex": "^AS[0-9]+"}, "fields": [ {"asndrop_country": {"regex": r"^.*;\W([a-zA-Z]+)\W+", "transform": r"\1"}}, {"asndrop_org": {"regex": r"^.*\|\W+(.*)", "transform": r"\1"}}, ], } } mocker.patch.object( demisto, "params", return_value={ "url": feed_url, "ignore_regex": "^;.*", "feed_url_to_config": feed_url_to_config, "feedTags": tags, "tlp_color": tlp_color, }, ) mocker.patch.object(demisto, "command", return_value="fetch-indicators") mocker.patch.object(demisto, "createIndicators") with open("test_data/asn_ranges.txt") as asn_ranges_txt: asn_ranges = asn_ranges_txt.read().encode("utf8") requests_mock.get(feed_url, content=asn_ranges) feed_main("great_feed_name") # verify createIndicators was called with 466 indicators assert demisto.createIndicators.call_count == 1 indicators = demisto.createIndicators.call_args[0][0] assert len(indicators) == 466 # verify one of the expected indicators assert { "rawJSON": { "asndrop_country": "US", "asndrop_org": "LAKSH CYBERSECURITY AND DEFENSE LLC", "tags": tags.split(","), "trafficlightprotocol": "AMBER", "type": indicator_type, "value": "AS397539", }, "type": indicator_type, "value": "AS397539", "fields": {"tags": ["tag1", "tag2"], "trafficlightprotocol": "AMBER"}, } in indicators def test_feed_main_test_module(mocker, requests_mock): """ Given - Parameters (url, ignore_regex, feed_url_to_config and tags) to configure a feed. When - Running test-module (clicking on Test). Then - Ensure 'ok' is returned. """ feed_url = "https://www.spamhaus.org/drop/asndrop.txt" indicator_type = "ASN" tags = "tag1,tag2" tlp_color = "AMBER" feed_url_to_config = { "https://www.spamhaus.org/drop/asndrop.txt": { "indicator_type": indicator_type, "indicator": {"regex": "^AS[0-9]+"}, "fields": [ {"asndrop_country": {"regex": r"^.*;\W([a-zA-Z]+)\W+", "transform": r"\1"}}, {"asndrop_org": {"regex": r"^.*\|\W+(.*)", "transform": r"\1"}}, ], } } mocker.patch.object( demisto, "params", return_value={ "url": feed_url, "ignore_regex": "^;.*", "feed_url_to_config": feed_url_to_config, "feedTags": tags, "tlp_color": tlp_color, }, ) mocker.patch.object(demisto, "command", return_value="test-module") mocker.patch.object(demisto, "results") with open("test_data/asn_ranges.txt") as asn_ranges_txt: asn_ranges = asn_ranges_txt.read().encode("utf8") requests_mock.get(feed_url, content=asn_ranges) feed_main("great_feed_name") assert demisto.results.call_count == 1 results = demisto.results.call_args[0][0] assert results["HumanReadable"] == "ok" def test_get_indicators_with_relations(): """ Given: - feed url config including relations values When: - Fetching indicators - create_relationships param is set to True Then: - Validate the returned list of indicators return relationships. """ feed_url_to_config = { "https://www.spamhaus.org/drop/asndrop.txt": { "indicator_type": "IP", "indicator": {"regex": r"^.+,\"?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\"?", "transform": "\\1"}, "relationship_name": "indicator-of", "relationship_entity_b_type": "STIX Malware", "fields": [ { "firstseenbysource": {"regex": r"^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})", "transform": "\\1"}, "port": {"regex": r"^.+,.+,(\d{1,5}),", "transform": "\\1"}, "updatedate": {"regex": r"^.+,.+,.+,(\d{4}-\d{2}-\d{2})", "transform": "\\1"}, "malwarefamily": {"regex": r"^.+,.+,.+,.+,(.+)", "transform": "\\1"}, "relationship_entity_b": {"regex": r"^.+,.+,.+,.+,\"(.+)\"", "transform": "\\1"}, } ], } } expected_res = ( [ { "value": "127.0.0.1", "type": "IP", "rawJSON": { "malwarefamily": '"Test"', "relationship_entity_b": "Test", "value": "127.0.0.1", "type": "IP", "tags": [], }, "relationships": [ { "name": "indicator-of", "reverseName": "indicated-by", "type": "IndicatorToIndicator", "entityA": "127.0.0.1", "entityAFamily": "Indicator", "entityAType": "IP", "entityB": "Test", "entityBFamily": "Indicator", "entityBType": "Malware", "fields": {}, } ], "fields": {"tags": []}, } ], True, ) asn_ranges = '"2021-01-17 07:44:49","127.0.0.1","3889","online","2021-04-22","Test"' with requests_mock.Mocker() as m: m.get("https://www.spamhaus.org/drop/asndrop.txt", content=asn_ranges.encode("utf-8")) client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", source_name="spamhaus", ignore_regex="^;.*", feed_url_to_config=feed_url_to_config, indicator_type="ASN", ) indicators = fetch_indicators_command( client, feed_tags=[], tlp_color=[], itype="IP", auto_detect=False, create_relationships=True ) assert indicators == expected_res def test_get_indicators_without_relations(): """ Given: - feed url config including relations values When: - Fetching indicators - create_relationships param is set to False Then: - Validate the returned list of indicators dont return relationships. """ feed_url_to_config = { "https://www.spamhaus.org/drop/asndrop.txt": { "indicator_type": "IP", "indicator": {"regex": r"^.+,\"?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\"?", "transform": "\\1"}, "relationship_name": "indicator-of", "relationship_entity_b_type": "STIX Malware", "fields": [ { "firstseenbysource": {"regex": r"^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})", "transform": "\\1"}, "port": {"regex": r"^.+,.+,(\d{1,5}),", "transform": "\\1"}, "updatedate": {"regex": r"^.+,.+,.+,(\d{4}-\d{2}-\d{2})", "transform": "\\1"}, "malwarefamily": {"regex": r"^.+,.+,.+,.+,(.+)", "transform": "\\1"}, "relationship_entity_b": {"regex": r"^.+,.+,.+,.+,\"(.+)\"", "transform": "\\1"}, } ], } } expected_res = ( [ { "value": "127.0.0.1", "type": "IP", "rawJSON": { "malwarefamily": '"Test"', "relationship_entity_b": "Test", "value": "127.0.0.1", "type": "IP", "tags": [], }, "fields": {"tags": []}, } ], True, ) asn_ranges = '"2021-01-17 07:44:49","127.0.0.1","3889","online","2021-04-22","Test"' with requests_mock.Mocker() as m: m.get("https://www.spamhaus.org/drop/asndrop.txt", content=asn_ranges.encode("utf-8")) client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", source_name="spamhaus", ignore_regex="^;.*", feed_url_to_config=feed_url_to_config, indicator_type="ASN", ) indicators = fetch_indicators_command( client, feed_tags=[], tlp_color=[], itype="IP", auto_detect=False, create_relationships=False ) assert indicators == expected_res def test_fetch_indicators_exclude_enrichment(): """ Given: - Exclude enrichment parameter is used When: - Calling the fetch_indicators_command Then: - The indicators should include the enrichmentExcluded field if exclude is True. """ feed_url_to_config = { "https://www.spamhaus.org/drop/asndrop.txt": { "indicator_type": "IP", "indicator": {"regex": r"^.+,\"?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\"?", "transform": "\\1"}, "relationship_name": "indicator-of", "relationship_entity_b_type": "STIX Malware", "fields": [ { "firstseenbysource": {"regex": r"^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})", "transform": "\\1"}, "port": {"regex": r"^.+,.+,(\d{1,5}),", "transform": "\\1"}, "updatedate": {"regex": r"^.+,.+,.+,(\d{4}-\d{2}-\d{2})", "transform": "\\1"}, "malwarefamily": {"regex": r"^.+,.+,.+,.+,(.+)", "transform": "\\1"}, "relationship_entity_b": {"regex": r"^.+,.+,.+,.+,\"(.+)\"", "transform": "\\1"}, } ], } } expected_res = ( [ { "value": "127.0.0.1", "type": "IP", "rawJSON": { "malwarefamily": '"Test"', "relationship_entity_b": "Test", "value": "127.0.0.1", "type": "IP", "tags": [], }, "fields": {"tags": []}, "enrichmentExcluded": True, } ], True, ) asn_ranges = '"2021-01-17 07:44:49","127.0.0.1","3889","online","2021-04-22","Test"' with requests_mock.Mocker() as m: m.get("https://www.spamhaus.org/drop/asndrop.txt", content=asn_ranges.encode("utf-8")) client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", source_name="spamhaus", ignore_regex="^;.*", feed_url_to_config=feed_url_to_config, indicator_type="ASN", ) indicators = fetch_indicators_command( client, feed_tags=[], tlp_color=[], itype="IP", auto_detect=False, create_relationships=False, enrichment_excluded=True, ) assert indicators == expected_res def test_fetch_indicators_ip_ranges_to_cidrs(): """ Given: - Text containing incidicators as IP ranges. When: - Calling the fetch_indicators_command Then: - CIDR indicators should be returned. """ feed_url_to_config = { "https://www.spamhaus.org/drop/asndrop.txt": { "indicator_type": "CIDR", "indicator": {"regex": r"^(\S+)-(\S+)$", "transform": "\\1-\\2"}, } } with open("test_data/expected_cidr_result.json") as expected_cidr_result: expected_res = (json.loads(expected_cidr_result.read()), True) ip_ranges = ( "14.14.14.14-14.14.14.14\n12.12.12.24-12.12.12.255\n198.51.100.0-198.51.100.255\nfe80::c000-fe80::cfff\n12.12.12.12" ) with requests_mock.Mocker() as m: m.get("https://www.spamhaus.org/drop/asndrop.txt", content=ip_ranges.encode("utf-8")) client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", source_name="spamhaus", feed_url_to_config=feed_url_to_config, indicator_type="CIDR", ) indicators = fetch_indicators_command(client, feed_tags=[], tlp_color=[], itype="CIDR", auto_detect=False) assert indicators == expected_res def test_fetch_indicators_ip_ranges_to_cidrs_convert_32_to_ip(): """ Given: - Text containing indicators as IP ranges - marking cidr_to_32_ip - returning 32 CIDR also as IP as true When: - Calling the fetch_indicators_command Then: - CIDR indicators should be returned and CIDR should also be returned as IP. """ feed_url_to_config = { "https://www.spamhaus.org/drop/asndrop.txt": { "indicator_type": "CIDR", "indicator": {"regex": r"^(\S+)-(\S+)$", "transform": "\\1-\\2"}, } } with open("test_data/expected_cidr_result.json") as expected_cidr_result: expected_res = (json.loads(expected_cidr_result.read()), True) cidr_as_ip_entry = { "fields": {"tags": []}, "rawJSON": {"tags": [], "type": "IP", "value": "14.14.14.14"}, "type": "IP", "value": "14.14.14.14", } # Insert as the second item (index 1) expected_res[0].insert(1, cidr_as_ip_entry) ip_ranges = ( "14.14.14.14-14.14.14.14\n12.12.12.24-12.12.12.255\n198.51.100.0-198.51.100.255\nfe80::c000-fe80::cfff\n12.12.12.12" ) with requests_mock.Mocker() as m: m.get("https://www.spamhaus.org/drop/asndrop.txt", content=ip_ranges.encode("utf-8")) client = Client( url="https://www.spamhaus.org/drop/asndrop.txt", source_name="spamhaus", feed_url_to_config=feed_url_to_config, indicator_type="CIDR", ) indicators = fetch_indicators_command( client, feed_tags=[], tlp_color=[], itype="CIDR", auto_detect=False, cidr_32_to_ip=True ) assert indicators == expected_res def test_get_no_update_value(mocker): """ Given - response with last_modified and etag headers with the same values like in the integration context. When - Running get_no_update_value method. Then - Ensure that the response is False """ mocker.patch.object(demisto, "debug") class MockResponse: headers = { "Last-Modified": "Fri, 30 Jul 2021 00:24:13 GMT", # guardrails-disable-line "ETag": "d309ab6e51ed310cf869dab0dfd0d34b", } # guardrails-disable-line status_code = 200 no_update = get_no_update_value(MockResponse(), "https://www.spamhaus.org/drop/asndrop.txt") assert not no_update assert ( demisto.debug.call_args[0][0] == "New indicators fetched - the Last-Modified value has been updated," " createIndicators will be executed with noUpdate=False." ) def test_get_no_update_value_etag_with_double_quotes(mocker): """ Given - response with etag header that contains double-quotes. When - Running get_no_update_value method. Then - Ensure that the etag value in setLastRun is without double-quotes. """ mocker.patch.object(demisto, "setLastRun") url = "https://www.spamhaus.org/drop/asndrop.txt" etag = "d309ab6e51ed310cf869dab0dfd0d34b" class MockResponse: headers = { "Last-Modified": "Fri, 30 Jul 2021 00:24:13 GMT", # guardrails-disable-line "ETag": f'"{etag}"', } # guardrails-disable-line status_code = 200 get_no_update_value(MockResponse(), url) assert demisto.setLastRun.mock_calls[0][1][0][url]["etag"] == etag def test_build_iterator_not_modified_header(mocker): """ Given - response with status code 304(Not Modified) When - Running build_iterator method. Then - Ensure that the results are empty and No_update value is True. """ mocker.patch.object(demisto, "debug") mocker.patch("CommonServerPython.get_demisto_version", return_value={"version": "6.5.0"}) with requests_mock.Mocker() as m: m.get("https://api.github.com/meta", status_code=304) client = Client(url="https://api.github.com/meta") result = client.build_iterator() assert result assert result[0]["https://api.github.com/meta"] assert list(result[0]["https://api.github.com/meta"]["result"]) == [] assert result[0]["https://api.github.com/meta"]["no_update"] assert demisto.debug.call_args[0][0] == "No new indicators fetched, createIndicators will be executed with noUpdate=True." def test_build_iterator_with_version_6_2_0(mocker): """ Given - server version 6.2.0 When - Running build_iterator method. Then - Ensure that the no_update value is True - Request is called without headers "If-None-Match" and "If-Modified-Since" """ mocker.patch.object(demisto, "debug") mocker.patch("CommonServerPython.get_demisto_version", return_value={"version": "6.2.0"}) with requests_mock.Mocker() as m: m.get("https://api.github.com/meta", status_code=304) client = Client(url="https://api.github.com/meta", headers={}) result = client.build_iterator() assert result[0]["https://api.github.com/meta"]["no_update"] assert list(result[0]["https://api.github.com/meta"]["result"]) == [] assert "If-None-Match" not in client.headers assert "If-Modified-Since" not in client.headers def test_get_no_update_value_without_headers(mocker): """ Given - response without last_modified and etag headers. When - Running get_no_update_value. Then - Ensure that the response is False. """ mocker.patch.object(demisto, "debug") mocker.patch("CommonServerPython.get_demisto_version", return_value={"version": "6.5.0"}) class MockResponse: headers = {} status_code = 200 no_update = get_no_update_value(MockResponse(), "https://www.spamhaus.org/drop/asndrop.txt") assert not no_update assert ( demisto.debug.call_args[0][0] == "Last-Modified and Etag headers are not exists," "createIndicators will be executed with noUpdate=False." ) @pytest.mark.parametrize( "has_passed_time_threshold_response, expected_result", [(True, None), (False, {"If-None-Match": "etag", "If-Modified-Since": "2023-05-29T12:34:56Z"})], ) def test_build_iterator__with_and_without_passed_time_threshold(mocker, has_passed_time_threshold_response, expected_result): """ Given - A boolean result from the has_passed_time_threshold function When - Running build_iterator method. Then - Ensure the next request headers will be as expected: case 1: has_passed_time_threshold_response is True, no headers will be added case 2: has_passed_time_threshold_response is False, headers containing 'last_modified' and 'etag' will be added """ mocker.patch("CommonServerPython.get_demisto_version", return_value={"version": "6.5.0"}) mock_session = mocker.patch.object(requests, "get") mocker.patch("HTTPFeedApiModule.has_passed_time_threshold", return_value=has_passed_time_threshold_response) mocker.patch( "demistomock.getLastRun", return_value={ "https://api.github.com/meta": { "etag": "etag", "last_modified": "2023-05-29T12:34:56Z", "last_updated": "2023-05-05T09:09:06Z", } }, ) client = Client(url="https://api.github.com/meta", credentials={"identifier": "user", "password": "password"}) client.build_iterator() assert mock_session.call_args[1].get("headers") == expected_result def test_build_iterator_etag_with_double_quotes(mocker): """ Given - getLastRun with etag header that contains double-quotes. When - Running build_iterator method. Then - Ensure the next request header contains 'etag' without double-quotes. """ etag = "d309ab6e51ed310cf869dab0dfd0d34b" mocker.patch("CommonServerPython.get_demisto_version", return_value={"version": "6.5.0"}) mock_session = mocker.patch.object(requests, "get") mocker.patch("HTTPFeedApiModule.has_passed_time_threshold", return_value=False) mocker.patch( "demistomock.getLastRun", return_value={ "https://api.github.com/meta": { "etag": f'"{etag}"', "last_modified": "2023-05-29T12:34:56Z", "last_updated": "2023-05-05T09:09:06Z", } }, ) client = Client(url="https://api.github.com/meta", credentials={"identifier": "user", "password": "password"}) client.build_iterator() assert mock_session.call_args[1]["headers"]["If-None-Match"] == etag def test_feed_main_enrichment_excluded(mocker): """ Given: params with tlp_color set to RED and enrichmentExcluded set to False When: Calling feed_main Then: validate enrichment_excluded is set to True """ from HTTPFeedApiModule import feed_main params = {"tlp_color": "RED", "enrichmentExcluded": False} feed_name = "test_feed" prefix = "test_prefix" with patch("HTTPFeedApiModule.Client") as client_mock: client_instance = mocker.Mock() client_mock.return_value = client_instance fetch_indicators_command_mock = mocker.patch("HTTPFeedApiModule.fetch_indicators_command", return_value=([], None)) mocker.patch("HTTPFeedApiModule.is_xsiam_or_xsoar_saas", return_value=True) mocker.patch.object(demisto, "command", return_value="fetch-indicators") mocker.patch.object(demisto, "params", return_value=params) # Call the function under test feed_main(feed_name, params, prefix) # Assertion - verify that enrichment_excluded is set to True assert fetch_indicators_command_mock.call_args.kwargs["enrichment_excluded"] is True