DHS Feed v2
The Cybersecurity and Infrastructure Security Agency’s (CISA’s) free Automated Indicator Sharing (AIS) capability enables the exchange of cyber threat indicators, at machine speed, to the Federal Government community.
Data Enrichment & Threat Intelligence · DHS Feed · Feed
Details
| ID | DHS Feed v2 |
|---|---|
| Provider | DHS |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.5.0 |
| Docker Image | demisto/taxii2:1.0.0.9117276 |
| Supported Modules | Agentix XSIAM |
README
The Cybersecurity and Infrastructure Security Agency’s (CISA’s) free Automated Indicator Sharing (AIS) capability enables the exchange of cyber threat indicators, at machine speed, to the Federal Government community.
Use this version if your certificate supports TAXII 2 protocol.
Some changes have been made that might affect your existing content.
If you are upgrading from a previous version of this integration, see Breaking Changes.
Configure DHS Feed v2 in Cortex
| Parameter | Description | Required |
|---|---|---|
| Fetch indicators | False | |
| Discovery Service URL (e.g., https://ais2.cisa.dhs.gov/taxii2/) | True | |
| Key File as Text | For more information, visit https://us-cert.cisa.gov/ais. | True |
| Certificate File as Text | For more information, visit https://us-cert.cisa.gov/ais. | True |
| Default API Root to use | The default API root to use (e.g., default, public). If left empty, the server default API root will be used. When the server has no default root, the first available API root will be used instead. The user must be authorized to reach the selected API root. | False |
| Collection Name To Fetch Indicators From | Indicators will be fetched from this collection. Run the “dhs-get-collections” command to get a valid value. If left empty, the instance will try to fetch from all the collections in the given discovery service. | 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 |
| Feed Fetch Interval | False | |
| First Fetch Time | The time interval for the first fetch (retroactive) in the following format: <number> <time unit> of type minute/hour/day. For example, 1 minute, 12 hour. Limited to 48 hours. | False |
| STIX Objects To Fetch | The objects to fetch, most likely indicators. Might slow down fetch time. | 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 |
| Max Indicators Per Fetch | The maximum number of indicators that can be fetched per fetch. If this field is left empty, there will be no limit on the number of indicators fetched. | False |
| Max STIX Objects Per Poll | Set the number of STIX objects that will be requested with each TAXII poll (http request). A single fetch is made of several TAXII polls. Changing this setting can help speed up fetches, or fix issues on slower networks. Please note server restrictions may apply, overriding and limiting the requested limit. | False |
| Complex Observation Mode | Choose how to handle complex observations. Two or more Observation Expressions MAY be combined using a complex observation operator such as “AND”, “OR”. For example, `[ IP = ‘b’ ] AND [ URL = ‘d’ ]` | False |
| Tags | Supports CSV values. | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
dhs-get-indicators
Allows you to test your feed and to make sure you can fetch indicators successfully.
Due to API limitations, running this command may take longer than the default 5 minutes.
To overcome this issue increase the execution-timeout from 300 seconds to a higher value, the recommended value is 1800 seconds.
Base Command
dhs-get-indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| raw | Will return only the rawJSON of the indicator object. Possible values are: true, false. Default is false. | Optional |
| limit | Maximum number of indicators to return. Default is 10. | Optional |
| added_after | Fetch only indicators that were added to the server after the given time. Provide a <number> and <time unit> of type minute/hour/day. For example, 1 minute, 12 hour, 24 days. Limited to 48 hours. Default is 24 hours. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| DHS.Indicators.value | String | Indicator value. |
| DHS.Indicators.type | String | Indicator type. |
| DHS.Indicators.rawJSON | String | Indicator rawJSON. |
Command Example
!dhs-get-indicators limit=3 execution-timeout=1800
Context Example
{
"DHS.Indicators": [
{
"fields": {
"tags": [
"cisa-proprietary-false"
]
},
"rawJSON": {
"created": "2021-08-09T00:42:54.000Z",
"created_by_ref": "identity--e8",
"id": "indicator--e0",
"indicator_types": [
"anomalous-activity",
"attribution"
],
"labels": [
"cisa-proprietary-false"
],
"modified": "2021-09-26T04:16:13.000Z",
"name": "sometimes",
"object_marking_refs": [
"marking-definition--633",
"marking-definition--f51"
],
"pattern": "[domain-name:value = 'coronashop.jp']",
"pattern_type": "stix",
"pattern_version": "2.1",
"spec_version": "2.1",
"type": "Domain",
"valid_from": "2021-09-26T00:09:38Z",
"value": "coronashop.jp"
},
"type": "Domain",
"value": "coronashop.jp"
},
{
"fields": {
"description": "A totally famous IP Address",
"tags": [
"elevated"
]
},
"rawJSON": {
"created": "2022-03-01T14:17:59.000Z",
"created_by_ref": "identity--a9",
"description": "A totally famous IP Address",
"id": "indicator--2f5",
"labels": [
"elevated"
],
"modified": "2022-03-01T14:17:59.000Z",
"object_marking_refs": [
"marking-definition--633"
],
"pattern": "[ipv4-addr:value = '1.1.1.1']",
"pattern_type": "stix",
"spec_version": "2.1",
"type": "IP",
"valid_from": "2022-03-01T14:17:59.000000Z",
"value": "1.1.1.1"
},
"type": "IP",
"value": "1.1.1.1"
},
{
"fields": {
"tags": [
"elevated"
]
},
"rawJSON": {
"created": "2022-02-28T13:18:49.000Z",
"created_by_ref": "identity--8c4",
"id": "indicator--9a6",
"indicator_types": [
"file-hash-watchlist"
],
"labels": [
"elevated"
],
"modified": "2022-02-28T13:18:49.000Z",
"object_marking_refs": [
"marking-definition--633"
],
"pattern": "[file:hashes.MD5 = 'e6ecb146f469d243945ad8a5451ba1129c5b190f7d50c64580dbad4b8246f88e']",
"pattern_type": "stix",
"spec_version": "2.1",
"type": "File",
"valid_from": "2022-02-28T13:18:49.000000Z",
"value": "e6ecb146f469d243945ad8a5451ba1129c5b190f7d50c64580dbad4b8246f88e"
},
"type": "File",
"value": "e6ecb146f469d243945ad8a5451ba1129c5b190f7d50c64580dbad4b8246f88e"
}
]
}
Human Readable Output
Found 3 results added after 2022-12-07T10:29:13.079493Z UTC:
DHS Indicators
value type coronashop.jp Domain 1.1.1.1 IP e6ecb146f469d243945ad8a5451ba1129c5b190f7d50c64580dbad4b8246f88e File
dhs-get-collections
Gets the list of collections from the discovery service.
Base Command
dhs-get-collections
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| DHS.Collections.ID | String | Collection ID. |
| DHS.Collections.Name | String | Collection name. |
Command Example
#### Context Example
```json
{
"DHS.Collections": [
{
"ID": "3",
"Name": "Public Collection"
}
]
}
Human Readable Output
DHS Server Collections
Name ID Public Collection 3
Breaking Changes
The following are the breaking changes from the previous version of this integration.
Arguments
The following argument was removed in this version
In the dhs-get-indicators command, tlp_colorwas removed.
The behavior of the following arguments was changed
In the dhs-get-indicators command, the default value of the limit argument was changed to ‘10’.
Outputs
The following outputs were removed in this version
In the dhs-get-indicators command:
- DHS.type - this output was replaced by DHS.Indicators.type.
- DHS.value - this output was replaced by DHS.Indicators.value.
- DHS.tlp - this output was removed.
Additional Considerations for this version
Use this version if your certificate supports TAXII 2 protocol.
Known Limitations
“First Fetch Time” parameter can be configured for a maximum of 48 hours, due to limitations in DHS TAXII2 API.
Therefore, it is not possible to fetch indicators that last appeared in the feed more than 48 hours ago.
Configuration parameters
feed— Fetch indicatorsurl— Discovery Service URL (e.g., https://ais2.cisa.dhs.gov/taxii2/) (required)key— (required)certificate— Certificate File as Text (required)default_api_root— Default API Root to usecollection_to_fetch— Collection Name To Fetch Indicators FromfeedReputation— Indicator ReputationfeedReliability— Source Reliability (required)tlp_color— Traffic Light Protocol ColorfeedExpirationPolicy—feedExpirationInterval—feedFetchInterval— Feed Fetch Intervalinitial_interval— First Fetch Timeobjects_to_fetch— STIX Objects To Fetchinsecure— Trust any certificate (not secure)proxy— Use system proxy settingsfeedBypassExclusionList— Bypass exclusion listlimit— Max Indicators Per Fetchlimit_per_request— Max STIX Objects Per Pollobservation_operator_mode— Complex Observation ModefeedTags— TagsfeedIncremental— Incremental Feed
Commands (2)
-
dhs-get-collectionsGets the list of collections from the discovery service.
-
dhs-get-indicatorsAllows you to test your feed and to make sure you can fetch indicators successfully. Due to API limitations, this command may take a long time to run. Make sure the 'execution-timeout' argument is increased. See the integration readme for further information.
import pytest from DHSFeedV2 import * from freezegun import freeze_time with open("test_data/results.json") as f: RESULTS_JSON = json.load(f) with open("test_data/cortex_indicators_1.json") as f: CORTEX_IOCS_1 = json.load(f) with open("test_data/cortex_indicators_1.json") as f: CORTEX_IOCS_2 = json.load(f) class MockCollection: def __init__(self, id_, title): self.id = id_ self.title = title class TestFetchIndicators: """ Scenario: Test fetch_indicators_command """ def test_single_no_context(self, mocker): """ Scenario: Test single collection fetch with no last run Given: - collection to fetch is available and set to 'default' - there is no integration context - limit is -1 - initial interval is `1 day` When: - fetch_indicators_command is called Then: - update last run with latest collection fetch time """ mock_client = Taxii2FeedClient(url="", collection_to_fetch="default", proxies=[], verify=False, objects_to_fetch=[]) default_id = 1 nondefault_id = 2 mock_client.collections = [MockCollection(default_id, "default"), MockCollection(nondefault_id, "not_default")] mock_client.collection_to_fetch = mock_client.collections[0] mocker.patch.object(mock_client, "build_iterator", return_value=RESULTS_JSON) indicators, last_run = fetch_indicators_command(mock_client, -1, {}, "1 day") assert indicators == RESULTS_JSON assert mock_client.collection_to_fetch.id in last_run def test_single_with_context(self, mocker): """ Scenario: Test single collection fetch with no last run context Given: - collection to fetch is available and set to 'default' - there is an integration context, with 2 collections - limit is -1 - initial interval is `1 day` When: - fetch_indicators_command is called Then: - update last run with latest collection fetch time - don't update collection that wasn't fetched from """ mock_client = Taxii2FeedClient(url="", collection_to_fetch="default", proxies=[], verify=False, objects_to_fetch=[]) default_id = 1 nondefault_id = 2 mock_client.collections = [MockCollection(default_id, "default"), MockCollection(nondefault_id, "not_default")] mock_client.collection_to_fetch = mock_client.collections[0] last_run = {mock_client.collections[1]: "test"} mocker.patch.object(mock_client, "build_iterator", return_value=RESULTS_JSON) indicators, last_run = fetch_indicators_command(mock_client, -1, last_run, "1 day") assert indicators == RESULTS_JSON assert mock_client.collection_to_fetch.id in last_run assert last_run.get(mock_client.collections[1]) == "test" def test_multi_no_context(self, mocker): """ Scenario: Test multi collection fetch with no last run Given: - collection to fetch is set to None - there is no integration context - limit is -1 - initial interval is `1 day` When: - fetch_indicators_command is called Then: - fetch 14 indicators - update last run with latest collection fetch time """ mock_client = Taxii2FeedClient(url="", collection_to_fetch=None, proxies=[], verify=False, objects_to_fetch=[]) default_id = 1 nondefault_id = 2 mock_client.collections = [MockCollection(default_id, "default"), MockCollection(nondefault_id, "not_default")] mocker.patch.object(mock_client, "build_iterator", side_effect=[CORTEX_IOCS_1, CORTEX_IOCS_2]) indicators, last_run = fetch_indicators_command(mock_client, -1, {}, "1 day") assert len(indicators) == 14 assert mock_client.collection_to_fetch.id in last_run def test_multi_with_context(self, mocker): """ Scenario: Test multi collection fetch with no last run Given: - collection to fetch is set to None - there is no integration context - limit is len(CORTEX_IOCS_1) - initial interval is `1 day` When: - fetch_indicators_command is called Then: - fetch 7 indicators - update last run with latest collection fetch time """ mock_client = Taxii2FeedClient(url="", collection_to_fetch=None, proxies=[], verify=False, objects_to_fetch=[]) id_1 = 1 id_2 = 2 mock_client.collections = [MockCollection(id_1, "a"), MockCollection(id_2, "b")] last_run = {mock_client.collections[1]: "test"} mocker.patch.object(mock_client, "build_iterator", side_effect=[CORTEX_IOCS_1, CORTEX_IOCS_2]) indicators, last_run = fetch_indicators_command(mock_client, len(CORTEX_IOCS_1), last_run, "1 day") assert len(indicators) == len(CORTEX_IOCS_1) assert last_run.get(mock_client.collections[1]) == "test" def test_chinese_char(self, mocker): """ Scenario: Test single collection fetch that raises an InvalidJSONError because the response is "筽" Given: - collection to fetch is available and set to 'default' - there is no integration context - limit is -1 - initial interval is `1 day` When: - fetch_indicators_command is called Then: - returns an empty list of indicators """ mock_client = Taxii2FeedClient(url="", collection_to_fetch="default", proxies=[], verify=False, objects_to_fetch=[]) mocker.patch.object(mock_client, "collection_to_fetch", spec=v20.Collection) mocker.patch.object(mock_client, "load_stix_objects_from_envelope", side_effect=InvalidJSONError) indicators, last_run = fetch_indicators_command(mock_client, -1, {}, "1 day") assert indicators == [] assert mock_client.collection_to_fetch.id in last_run def test_get_collections_command(): """ Given: - A taxii2 feed with collections When: - Running 'dhs-get-collections' command Then: - Returns the collections that the feed has """ mock_client = Taxii2FeedClient(url="", collection_to_fetch=None, proxies=[], verify=False, objects_to_fetch=[]) mock_client.collections = [MockCollection("first id", "first name"), MockCollection("second id", "second name")] result = get_collections_command(mock_client) assert result.outputs == [{"Name": "first name", "ID": "first id"}, {"Name": "second name", "ID": "second id"}] no_collections = (False, "24 hours", "Could not connect to server") all_parameters_good = (True, "24 hours", "ok") large_time_interval = (True, "3 days", 'Due to DHS API limitations, "First Fetch Time" is limited to 48 hours.') @pytest.mark.parametrize( "has_collections, initial_interval_input, expected_output", [ no_collections, all_parameters_good, large_time_interval, ], ) def test_command_test_module(has_collections, initial_interval_input, expected_output, mocker): """ Given: - All integration parameters When: - Running 'test-module' command Then: - Returns the relevant message according to the given parameters values """ mock_client = Taxii2FeedClient(url="", collection_to_fetch=None, proxies=[], verify=False, objects_to_fetch=[]) mock_client.collections = [MockCollection("first id", "first name")] if has_collections else None mocker.patch.object(mock_client, "initialise") mocker.patch.object(mock_client, "build_iterator", return_value=[]) result = command_test_module(mock_client, initial_interval_input) assert result == expected_output less_then_max = ("24 hours", None, "24 hours") a_bit_less_then_max = ("44 hours", None, "44 hours") more_then_max = ("57 hours", None, MAX_FETCH_INTERVAL) take_closer_second_value = ("2 hours", "1 hour", "1 hour") take_closer_first_value = ("1 hour", "2 hours", "1 hour") @freeze_time("2022-11-23 11:00:00 UTC") @pytest.mark.parametrize( "given_interval, fetch_interval, expected_min_interval", [less_then_max, a_bit_less_then_max, more_then_max, take_closer_second_value, take_closer_first_value], ) def test_get_limited_interval(given_interval, fetch_interval, expected_min_interval): """ Given: - Two time intervals When: - Running fetch indicators Then: - Returns the closer time """ returned_min_interval = get_limited_interval(given_interval, fetch_interval) expected_min_interval = dateparser.parse(expected_min_interval, date_formats=[TAXII_TIME_FORMAT]) assert returned_min_interval.replace(microsecond=0) == expected_min_interval.replace(microsecond=0, tzinfo=utc) take_min_sent_with_value = ("3 days", "50 hours", MAX_FETCH_INTERVAL) take_min_sent_without_value = (None, "50 hours", DEFAULT_FETCH_INTERVAL) @freeze_time("2022-11-23 11:00:00 UTC") @pytest.mark.parametrize( "given_interval, fetch_interval, expected_min_interval", [take_min_sent_with_value, take_min_sent_without_value] ) def test_get_limited_interval_twice(given_interval, fetch_interval, expected_min_interval): """ Given: - Two time intervals, one which passes get_limited_interval twice When: - Running fetch indicators with initial_interval value Then: - Returns the closer time """ returned_min_interval = get_limited_interval(get_limited_interval(given_interval or DEFAULT_FETCH_INTERVAL), fetch_interval) expected_min_interval = dateparser.parse(expected_min_interval, date_formats=[TAXII_TIME_FORMAT]) assert returned_min_interval.replace(microsecond=0) == expected_min_interval.replace(microsecond=0, tzinfo=utc) human_to_datetime = ("24 hours", datetime(2022, 11, 22, 11, 00, 00, tzinfo=utc)) timestamp_to_datetime = ("2022-11-30T00:28:24Z", datetime(2022, 11, 30, 00, 28, 24, tzinfo=utc)) datetime_to_datetime = (datetime(2022, 11, 30, 00, 28, 24, tzinfo=utc), datetime(2022, 11, 30, 00, 28, 24, tzinfo=utc)) none_or_human_to_datetime = (None or MAX_FETCH_INTERVAL, datetime(2022, 11, 21, 11, 00, 00, tzinfo=utc)) timestamp_or_none_to_datetime = ("2022-11-30T00:28:24Z", datetime(2022, 11, 30, 00, 28, 24, tzinfo=utc)) datetime_or_human_to_datetime = ( datetime(2022, 11, 30, 00, 28, 24, tzinfo=utc) or "24 hours", datetime(2022, 11, 30, 00, 28, 24, tzinfo=utc), ) @freeze_time("2022-11-23 11:00:00 UTC") # works only with lint @pytest.mark.parametrize( "given_interval, expected_datetime", [ human_to_datetime, timestamp_to_datetime, datetime_to_datetime, none_or_human_to_datetime, timestamp_or_none_to_datetime, datetime_or_human_to_datetime, ], ) def test_get_datetime(given_interval, expected_datetime): """ Given: - Time interval When: - Turning it into datetime Then: - Returns the corresponding datetime """ assert get_datetime(given_interval) == expected_datetime def test_get_datetime_invalid(): """ Given: - Invalid time interval When: - Turning it into datetime Then: - Raises an error """ with pytest.raises(DemistoException, match="Given time interval is not in a valid format."): get_datetime("3 yamim")