From 4a246d6b9ad79b490a9eff0009b899f187f5006f Mon Sep 17 00:00:00 2001 From: michal-dagan <109464765+michal-dagan@users.noreply.github.com> Date: Thu, 9 Mar 2023 13:00:31 +0100 Subject: [PATCH] [TAXII 2 Server] Revoke objects, Support SCO structure (#24939) Change the default order of retrieved objects to ascending Add relationships objects --- Packs/Base/.pack-ignore | 1 + Packs/Base/ReleaseNotes/1_31_72.md | 4 + .../CommonServerPython/CommonServerPython.py | 12 +- .../CommonServerPython_test.py | 24 ++ Packs/Base/pack_metadata.json | 2 +- Packs/TAXIIServer/.secrets-ignore | 5 +- .../Integrations/TAXII2Server/README.md | 16 +- .../Integrations/TAXII2Server/TAXII2Server.py | 95 ++++- .../TAXII2Server/TAXII2Server.yml | 2 +- .../TAXII2Server/TAXII2Server_test.py | 37 ++ .../TAXII2Server/test_data/entity_b_iocs.json | 285 +++++++++++++ .../objects21_ip_with_relationships.json | 33 ++ .../searchRelationships-response.json | 152 +++++++ .../TAXII2Server/test_data/sort_ip_iocs.json | 392 ++++++++++++++++++ Packs/TAXIIServer/ReleaseNotes/2_0_28.md | 7 + Packs/TAXIIServer/pack_metadata.json | 2 +- 16 files changed, 1053 insertions(+), 16 deletions(-) create mode 100644 Packs/Base/ReleaseNotes/1_31_72.md create mode 100644 Packs/TAXIIServer/Integrations/TAXII2Server/test_data/entity_b_iocs.json create mode 100644 Packs/TAXIIServer/Integrations/TAXII2Server/test_data/objects21_ip_with_relationships.json create mode 100644 Packs/TAXIIServer/Integrations/TAXII2Server/test_data/searchRelationships-response.json create mode 100644 Packs/TAXIIServer/Integrations/TAXII2Server/test_data/sort_ip_iocs.json create mode 100644 Packs/TAXIIServer/ReleaseNotes/2_0_28.md diff --git a/Packs/Base/.pack-ignore b/Packs/Base/.pack-ignore index 2365595e3f6e..dce5dda05b24 100644 --- a/Packs/Base/.pack-ignore +++ b/Packs/Base/.pack-ignore @@ -64,3 +64,4 @@ SSLAdapter SSL appendContext refang +IndicatorsSearcher diff --git a/Packs/Base/ReleaseNotes/1_31_72.md b/Packs/Base/ReleaseNotes/1_31_72.md new file mode 100644 index 000000000000..9e64a25dbe56 --- /dev/null +++ b/Packs/Base/ReleaseNotes/1_31_72.md @@ -0,0 +1,4 @@ + +#### Scripts +##### CommonServerPython +- Added support for the *sort* parameter in the **IndicatorsSearcher** class, which enables sorting of retrieved indicators based on specific criteria. \ No newline at end of file diff --git a/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py b/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py index ff5b24e2d5be..1fc70eb96e63 100644 --- a/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py +++ b/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py @@ -9428,6 +9428,9 @@ class IndicatorsSearcher: :type limit: ``Optional[int]`` :param limit: the current upper limit of the search (can be updated after init) + :type sort: ``List[Dict]`` + :param sort: An array of sort params ordered by importance. Item structure: {"field": string, "asc": boolean} + :return: No data returned :rtype: ``None`` """ @@ -9441,7 +9444,9 @@ def __init__(self, size=100, to_date=None, value='', - limit=None): + limit=None, + sort=None, + ): # searchAfter is available in searchIndicators from version 6.1.0 self._can_use_search_after = is_demisto_version_ge('6.1.0') # populateFields merged in https://github.com/demisto/server/pull/18398 @@ -9457,6 +9462,7 @@ def __init__(self, self._value = value self._limit = limit self._total_iocs_fetched = 0 + self._sort = sort def __iter__(self): return self @@ -9550,8 +9556,10 @@ def search_indicators_by_version(self, from_date=None, query='', size=100, to_da searchAfter=self._search_after_param if self._can_use_search_after else None, populateFields=self._filter_fields if self._can_use_filter_fields else None, # use paging as fallback when cannot use search_after - page=self.page if not self._can_use_search_after else None + page=self.page if not self._can_use_search_after else None, ) + if is_demisto_version_ge('6.6.0'): + search_args['sort'] = self._sort res = demisto.searchIndicators(**search_args) if isinstance(self._page, int): self._page += 1 # advance pages diff --git a/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py b/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py index 3014a927450b..6a736224e285 100644 --- a/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py +++ b/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py @@ -6736,6 +6736,30 @@ def test_iterator__research_flow(self, mocker): results.append(res) assert len(results) == 1 + def test_search_indicators_with_sort(self, mocker): + """ + Given: + - Searching indicators with a custom sort parameter. + - Mocking the searchIndicators function. + When: + - Calling the searchIndicators function with the custom sort parameter. + Then: + - Ensure that the sort parameter is set correctly. + - Ensure that the searchIndicators function is called with the expected arguments. + """ + from CommonServerPython import IndicatorsSearcher + get_demisto_version._version = None # clear cache between runs of the test + mocker.patch.object(demisto, 'demistoVersion', return_value={'version': '6.6.0'}) + + mocker.patch.object(demisto, 'searchIndicators') + sort_param = [{"field": "created", "asc": False}] + search_indicators_obj_search_after = IndicatorsSearcher(sort=sort_param) + search_indicators_obj_search_after.search_indicators_by_version() + expected_args = {'size': 100, 'sort': [{'asc': False, 'field': 'created'}]} + assert search_indicators_obj_search_after._sort == sort_param + demisto.searchIndicators.assert_called_once_with(**expected_args) + + class TestAutoFocusKeyRetriever: def test_instantiate_class_with_param_key(self, mocker, clear_version_cache): diff --git a/Packs/Base/pack_metadata.json b/Packs/Base/pack_metadata.json index f6a364912a1f..6498ceb7eb12 100644 --- a/Packs/Base/pack_metadata.json +++ b/Packs/Base/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Base", "description": "The base pack for Cortex XSOAR.", "support": "xsoar", - "currentVersion": "1.31.71", + "currentVersion": "1.31.72", "author": "Cortex XSOAR", "serverMinVersion": "6.0.0", "url": "https://www.paloaltonetworks.com/cortex", diff --git a/Packs/TAXIIServer/.secrets-ignore b/Packs/TAXIIServer/.secrets-ignore index 08621eb94f3b..9c7739de6102 100644 --- a/Packs/TAXIIServer/.secrets-ignore +++ b/Packs/TAXIIServer/.secrets-ignore @@ -5,4 +5,7 @@ http://www.rsyslog.com 18.163.0.0 ubuntu-appindicators@ubuntu.com http://host -test_taxii_wrong_auth \ No newline at end of file +test_taxii_wrong_auth +6.6.6.6 +@APP.route('/taxii2/' +uuid.uuid5(SCO_DET_ID_NAMESPACE \ No newline at end of file diff --git a/Packs/TAXIIServer/Integrations/TAXII2Server/README.md b/Packs/TAXIIServer/Integrations/TAXII2Server/README.md index d1c2b44ef6a3..c43f7a8e0930 100644 --- a/Packs/TAXIIServer/Integrations/TAXII2Server/README.md +++ b/Packs/TAXIIServer/Integrations/TAXII2Server/README.md @@ -54,14 +54,14 @@ For more information, visit [TAXII2 Documentation](http://docs.oasis-open.org/ct ## TAXII v2.1 API Enpoints -| **URL** | **Method** | **Response** | **TAXII2 Documentation** | -| --- | --- | --- | --- | -| /taxii2/ | GET | Server Discovery Information. | [Server Discovery](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107526) | -| /{api_root}/ | GET | XSOAR API root is *threatintel*. | [API Root Information](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107528) | -| /{api_root}/collections/ | GET | All Cortex XSOAR collections that configure in Collection JSON parameter. | [Collections Resource](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107533) | -| /{api_root}/collections/{collection_id}/ | GET | Cortex XSOAR Collection with given *collection_id*. | [Collection Response](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107535) | -| /{api_root}/collections/{collection_id}/manifest/ | GET | Object manifests from the given collection. | [Objects Manifest Resource](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107537) | -| /{api_root}/collections/{collection_id}/objects/ | GET | Objects (Cortex XSOAR Indicators) from the given collection. | [Object Resource](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107539) | +| **URL** | **Method** | **Response** | **TAXII2 Documentation** | +|---------------------------------------------------|------------|--------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| +| /taxii2/ | GET | Server Discovery Information. | [Server Discovery](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107526) | +| /{api_root}/ | GET | XSOAR API root is *threatintel*. | [API Root Information](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107528) | +| /{api_root}/collections/ | GET | All Cortex XSOAR collections that configure in Collection JSON parameter. | [Collections Resource](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107533) | +| /{api_root}/collections/{collection_id}/ | GET | Cortex XSOAR Collection with given *collection_id*. | [Collection Response](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107535) | +| /{api_root}/collections/{collection_id}/manifest/ | GET | Object manifests from the given collection. | [Objects Manifest Resource](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107537) | +| /{api_root}/collections/{collection_id}/objects/ | GET | Objects (Cortex XSOAR Indicators and Relationships) from the given collection. | [Object Resource](https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html#_Toc31107539) | For more information, visit [TAXII2 Documentation](https://docs.oasis-open.org/cti/taxii/v2.1/taxii-v2.1.html). diff --git a/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py b/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py index 7c7f2839ff9e..76d4e52f3529 100644 --- a/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py +++ b/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py @@ -567,6 +567,7 @@ def find_indicators(query: str, types: list, added_after, limit: int, offset: in new_limit = offset + limit iocs = [] extensions = [] + if is_manifest: field_filters: Optional[str] = ','.join(TAXII_REQUIRED_FILTER_FIELDS) elif SERVER.fields_to_present: @@ -582,7 +583,8 @@ def find_indicators(query: str, types: list, added_after, limit: int, offset: in query=new_query, limit=new_limit, size=PAGE_SIZE, - from_date=added_after + from_date=added_after, + sort=[{"field": "modified", "asc": True}], ) total = 0 @@ -606,7 +608,11 @@ def find_indicators(query: str, types: list, added_after, limit: int, offset: in extensions.append(extension_definition) elif stix_ioc: iocs.append(stix_ioc) - + if not is_manifest and iocs and is_demisto_version_ge('6.6.0'): + if relationships := create_relationships_objects(iocs, extensions): + total += len(relationships) + iocs.extend(relationships) + iocs = sorted(iocs, key=lambda k: k['modified']) return iocs, extensions, total @@ -1200,6 +1206,91 @@ def get_server_collections_command(integration_context): return result +def create_relationships_objects(stix_iocs: list[dict[str, Any]], extensions: list) -> list[dict[str, Any]]: + """ + Create entries for the relationships returned by the searchRelationships command. + :param stix_iocs: Entries for the Stix objects associated with given indicators + :param extensions: A list of dictionaries representing extension properties to include in the generated STIX objects. + :return: A list of dictionaries representing the relationships objects, including entityBs objects + """ + relationships_list: list[dict[str, Any]] = [] + iocs_value_to_id = {(stix_ioc.get('value') or stix_ioc.get('name')): stix_ioc.get('id') for stix_ioc in stix_iocs} + search_relationships = demisto.searchRelationships({'entities': list(iocs_value_to_id.keys())}).get('data') or [] + demisto.debug(f"Found {len(search_relationships)} relationships for {len(iocs_value_to_id)} Stix IOC values.") + + relationships_list.extend(create_entity_b_stix_objects(search_relationships, iocs_value_to_id, extensions)) + + for relationship in search_relationships: + + if not iocs_value_to_id.get(relationship.get('entityB')): + demisto.debug(f"WARNING: Invalid entity B - Relationships will not be created to entity A:" + f" {relationship.get('entityA')} with relationship name {relationship.get('name')}") + continue + try: + created_parsed = parse(relationship.get('createdInSystem')).strftime(STIX_DATE_FORMAT) + modified_parsed = parse(relationship.get('modified')).strftime(STIX_DATE_FORMAT) + except Exception as e: + created_parsed, modified_parsed = '', '' + demisto.debug(f"Error parsing dates for relationship {relationship.get('id')}: {e}") + + relationship_unique_id = uuid.uuid5(SERVER.namespace_uuid, f'relationship:{relationship.get("id")}') + relationship_stix_id = f'relationship--{relationship_unique_id}' + + relationship_object: dict[str, Any] = { + 'type': "relationship", + 'spec_version': SERVER.version, + 'id': relationship_stix_id, + 'created': created_parsed, + 'modified': modified_parsed, + "relationship_type": relationship.get('name'), + 'source_ref': iocs_value_to_id.get(relationship.get('entityA')), + 'target_ref': iocs_value_to_id.get(relationship.get('entityB')), + } + if description := demisto.get(relationship, 'CustomFields.description'): + relationship_object['Description'] = description + + relationships_list.append(relationship_object) + + return relationships_list + + +def create_entity_b_stix_objects(relationships: list[dict[str, Any]], iocs_value_to_id: dict, extensions: list) -> list: + """ + Generates a list of STIX objects for the 'entityB' values in the provided 'relationships' list. + :param relationships: A list of dictionaries representing relationships between entities + :param iocs_value_to_id: A dictionary mapping IOC values to their corresponding ID values. + :param extensions: A list of dictionaries representing extension properties to include in the generated STIX objects. + :return: A list of dictionaries representing STIX objects for the 'entityB' values + """ + entity_b_objects: list[dict[str, Any]] = [] + entity_b_values = "" + for relationship in relationships: + if (entity_b_value := relationship.get('entityB')) and entity_b_value not in iocs_value_to_id: + iocs_value_to_id[entity_b_value] = "" + entity_b_values += f'\"{entity_b_value}\" ' + if not entity_b_values: + return entity_b_objects + + found_indicators = demisto.searchIndicators(query=f'value:({entity_b_values})').get('iocs') or [] + + extensions_dict: dict = {} + for xsoar_indicator in found_indicators: + xsoar_type = xsoar_indicator.get('indicator_type') + stix_ioc, extension_definition, extensions_dict = create_stix_object(xsoar_indicator, xsoar_type, extensions_dict) + if XSOAR_TYPES_TO_STIX_SCO.get(xsoar_type) in SERVER.types_for_indicator_sdo: + stix_ioc = convert_sco_to_indicator_sdo(stix_ioc, xsoar_indicator) + if SERVER.has_extension and stix_ioc: + entity_b_objects.append(stix_ioc) + if extension_definition: + extensions.append(extension_definition) + elif stix_ioc: + entity_b_objects.append(stix_ioc) + iocs_value_to_id[(stix_ioc.get('value') or stix_ioc.get('name'))] = stix_ioc.get('id') + + demisto.debug(f"Generated {len(entity_b_objects)} STIX objects for 'entityB' values.") + return entity_b_objects + + def main(): # pragma: no cover """ Main diff --git a/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.yml b/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.yml index e03b391277be..c2169a4e1577 100644 --- a/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.yml +++ b/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.yml @@ -131,7 +131,7 @@ script: - contextPath: TAXIIServer.ServerInfo.description description: The server description type: String - dockerimage: demisto/flask-nginx:1.0.0.48906 + dockerimage: demisto/flask-nginx:1.0.0.49636 feed: false isfetch: false longRunning: true diff --git a/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server_test.py b/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server_test.py index 3d40e5c6f88a..83741c053ef2 100644 --- a/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server_test.py +++ b/Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server_test.py @@ -591,3 +591,40 @@ def test_convert_sco_to_indicator_sdo_with_type_file(mocker): assert 'file:hashes.' in output.get('pattern', '') assert 'MD5' in output.get('pattern', '') assert 'pattern_type' in output.keys() + + +def test_taxii21_objects_with_relationships(mocker, taxii2_server_v21): + """ + Given + TAXII Server v2.1, collection_id, no_extension + When + Calling get objects api request for given collection + Then + Validate that right objects are returned. + Ensure that searchRelationships is called with the expected arguments. + + """ + from CommonServerPython import get_demisto_version + + get_demisto_version._version = None # clear cache between runs of the test + mocker.patch.object(demisto, 'demistoVersion', return_value={'version': '6.6.0'}) + + mocker.patch('TAXII2Server.SERVER', taxii2_server_v21) + mocker.patch('TAXII2Server.SERVER.has_extension', False) + mock_search_relationships_response = util_load_json('test_data/searchRelationships-response.json') + mocker.patch.object(demisto, 'searchRelationships', return_value=mock_search_relationships_response) + + objects = util_load_json('test_data/objects21_ip_with_relationships.json') + mock_iocs = util_load_json('test_data/sort_ip_iocs.json') + mock_entity_b_iocs = util_load_json('test_data/entity_b_iocs.json') + mocker.patch.object(demisto, 'searchIndicators', side_effect=[mock_iocs, + mock_entity_b_iocs]) + + mocker.patch.object(demisto, 'params', return_value={'res_size': '4'}) + with APP.test_client() as test_client: + response = test_client.get('/threatintel/collections/4c649e16-2bb7-50f5-8826-2a2d0a0b9631/objects/', + headers=HEADERS) + assert response.status_code == 200 + assert response.content_type == 'application/taxii+json;version=2.1' + demisto.searchRelationships.assert_called_once_with({'entities': ["1.1.1.1", "8.8.8.8", "3.3.3.3", "1.2.3.4"]}) + assert response.json == objects diff --git a/Packs/TAXIIServer/Integrations/TAXII2Server/test_data/entity_b_iocs.json b/Packs/TAXIIServer/Integrations/TAXII2Server/test_data/entity_b_iocs.json new file mode 100644 index 000000000000..22a279428b88 --- /dev/null +++ b/Packs/TAXIIServer/Integrations/TAXII2Server/test_data/entity_b_iocs.json @@ -0,0 +1,285 @@ +{ + "iocs": [ + { + "CustomFields": { + "service": "Daily Threat Feed" + }, + "aggregatedReliability": "A - Completely reliable", + "calculatedTime": "2021-12-08T09:32:24.104143+02:00", + "comments": [ + { + "category": "Sighting", + "content": "Sighted", + "created": "2021-12-08T12:05:28.570995+02:00", + "entryId": "", + "id": "f814296f-6dca-4c88-86ad-68fd3871ecfa", + "modified": "0001-01-01T00:00:00Z", + "sortValues": null, + "source": "Some Feed.Some Feed_instance_1", + "type": "IndicatorCommentTimeLine", + "user": "@DBot", + "version": 0 + } + ], + "expiration": "2021-12-15T12:05:31.570698+02:00", + "expirationSource": { + "brand": "Some Feed", + "expirationInterval": 10080, + "expirationPolicy": "indicatorType", + "instance": "Some Feed_instance_1", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "setTime": "2021-12-08T12:05:28.570985+02:00", + "source": "indicatorType", + "user": "" + }, + "expirationStatus": "active", + "firstSeen": "0001-01-01T00:00:00Z", + "firstSeenEntryID": "API", + "id": "5188347", + "indicator_type": "IP", + "lastSeen": "0001-01-01T00:00:00Z", + "lastSeenEntryID": "API", + "modified": "2021-12-08T12:05:28.617414+02:00", + "modifiedTime": "2021-12-08T12:05:11+02:00", + "moduleToFeedMap": { + "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15": { + "ExpirationSource": { + "brand": "Some Feed", + "expirationInterval": 10080, + "expirationPolicy": "indicatorType", + "instance": "Some Feed_instance_1", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "setTime": "2021-12-08T12:05:28.570985+02:00", + "source": "indicatorType", + "user": "" + }, + "bypassExclusionList": false, + "classifierId": "", + "classifierVersion": 0, + "expirationInterval": 20160, + "expirationPolicy": "indicatorType", + "fetchTime": "2021-12-08T12:05:11+02:00", + "fields": { + "service": "Daily Threat Feed" + }, + "isEnrichment": false, + "mapperId": "", + "mapperVersion": 0, + "modifiedTime": "2021-12-08T12:05:11+02:00", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "rawJSON": null, + "reliability": "A - Completely reliable", + "score": 3, + "sourceBrand": "Some Feed", + "sourceInstance": "Some Feed_instance_1", + "timestamp": "0001-01-01T00:00:00Z", + "type": "IP", + "value": "2.2.2.2" + } + }, + "score": 3, + "sortValues": [ + " \u0001\u0016_-f0\u000b\u0019Q\u0018", + "5188347" + ], + "sourceBrands": [ + "Some Feed" + ], + "sourceInstances": [ + "Some Feed_instance_1" + ], + "timestamp": "2021-12-08T09:32:24.104143+02:00", + "value": "2.2.2.2", + "version": 15 + }, + { + "CustomFields": { + "service": "Daily Threat Feed" + }, + "aggregatedReliability": "A - Completely reliable", + "calculatedTime": "2021-12-08T09:32:24.104143+02:00", + "comments": [ + { + "category": "Sighting", + "content": "Sighted", + "created": "2021-12-08T12:05:28.570995+02:00", + "entryId": "", + "id": "f814296f-6dca-4c88-86ad-68fd3871ecfa", + "modified": "0001-01-01T00:00:00Z", + "sortValues": null, + "source": "Some Feed.Some Feed_instance_1", + "type": "IndicatorCommentTimeLine", + "user": "@DBot", + "version": 0 + } + ], + "expiration": "2021-12-15T12:05:31.570698+02:00", + "expirationSource": { + "brand": "Some Feed", + "expirationInterval": 10080, + "expirationPolicy": "indicatorType", + "instance": "Some Feed_instance_1", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "setTime": "2021-12-08T12:05:28.570985+02:00", + "source": "indicatorType", + "user": "" + }, + "expirationStatus": "active", + "firstSeen": "0001-01-01T00:00:00Z", + "firstSeenEntryID": "API", + "id": "5188347", + "indicator_type": "IP", + "lastSeen": "0001-01-01T00:00:00Z", + "lastSeenEntryID": "API", + "modified": "2021-12-08T12:05:28.617414+02:00", + "modifiedTime": "2021-12-08T12:05:11+02:00", + "moduleToFeedMap": { + "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15": { + "ExpirationSource": { + "brand": "Some Feed", + "expirationInterval": 10080, + "expirationPolicy": "indicatorType", + "instance": "Some Feed_instance_1", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "setTime": "2021-12-08T12:05:28.570985+02:00", + "source": "indicatorType", + "user": "" + }, + "bypassExclusionList": false, + "classifierId": "", + "classifierVersion": 0, + "expirationInterval": 20160, + "expirationPolicy": "indicatorType", + "fetchTime": "2021-12-08T12:05:11+02:00", + "fields": { + "service": "Daily Threat Feed" + }, + "isEnrichment": false, + "mapperId": "", + "mapperVersion": 0, + "modifiedTime": "2021-12-08T12:05:11+02:00", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "rawJSON": null, + "reliability": "A - Completely reliable", + "score": 3, + "sourceBrand": "Some Feed", + "sourceInstance": "Some Feed_instance_1", + "timestamp": "0001-01-01T00:00:00Z", + "type": "IP", + "value": "4.4.4.4" + } + }, + "score": 3, + "sortValues": [ + " \u0001\u0016_-f0\u000b\u0019Q\u0018", + "5188347" + ], + "sourceBrands": [ + "Some Feed" + ], + "sourceInstances": [ + "Some Feed_instance_1" + ], + "timestamp": "2021-12-08T09:32:24.104143+02:00", + "value": "4.4.4.4", + "version": 15 + }, + { + "CustomFields": { + "service": "Daily Threat Feed" + }, + "aggregatedReliability": "A - Completely reliable", + "calculatedTime": "2021-12-08T09:32:24.104143+02:00", + "comments": [ + { + "category": "Sighting", + "content": "Sighted", + "created": "2021-12-08T12:05:28.570995+02:00", + "entryId": "", + "id": "f814296f-6dca-4c88-86ad-68fd3871ecfa", + "modified": "0001-01-01T00:00:00Z", + "sortValues": null, + "source": "Some Feed.Some Feed_instance_1", + "type": "IndicatorCommentTimeLine", + "user": "@DBot", + "version": 0 + } + ], + "expiration": "2021-12-15T12:05:31.570698+02:00", + "expirationSource": { + "brand": "Some Feed", + "expirationInterval": 10080, + "expirationPolicy": "indicatorType", + "instance": "Some Feed_instance_1", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "setTime": "2021-12-08T12:05:28.570985+02:00", + "source": "indicatorType", + "user": "" + }, + "expirationStatus": "active", + "firstSeen": "0001-01-01T00:00:00Z", + "firstSeenEntryID": "API", + "id": "5188347", + "indicator_type": "IP", + "lastSeen": "0001-01-01T00:00:00Z", + "lastSeenEntryID": "API", + "modified": "2021-12-08T12:05:28.617414+02:00", + "modifiedTime": "2021-12-08T12:05:11+02:00", + "moduleToFeedMap": { + "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15": { + "ExpirationSource": { + "brand": "Some Feed", + "expirationInterval": 10080, + "expirationPolicy": "indicatorType", + "instance": "Some Feed_instance_1", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "setTime": "2021-12-08T12:05:28.570985+02:00", + "source": "indicatorType", + "user": "" + }, + "bypassExclusionList": false, + "classifierId": "", + "classifierVersion": 0, + "expirationInterval": 20160, + "expirationPolicy": "indicatorType", + "fetchTime": "2021-12-08T12:05:11+02:00", + "fields": { + "service": "Daily Threat Feed" + }, + "isEnrichment": false, + "mapperId": "", + "mapperVersion": 0, + "modifiedTime": "2021-12-08T12:05:11+02:00", + "moduleId": "fd8269e7-5ffa-4888-8ca8-bfe5eae78e15", + "rawJSON": null, + "reliability": "A - Completely reliable", + "score": 3, + "sourceBrand": "Some Feed", + "sourceInstance": "Some Feed_instance_1", + "timestamp": "0001-01-01T00:00:00Z", + "type": "IP", + "value": "6.6.6.6" + } + }, + "score": 3, + "sortValues": [ + " \u0001\u0016_-f0\u000b\u0019Q\u0018", + "5188347" + ], + "sourceBrands": [ + "Some Feed" + ], + "sourceInstances": [ + "Some Feed_instance_1" + ], + "timestamp": "2021-12-08T09:32:24.104143+02:00", + "value": "6.6.6.6", + "version": 15 + } + ], + "searchAfter": [ + " \u0001\u0016^>