SearchIndicatorRelationshipsAgentix
This automation outputs the indicator relationships to context according to the provided query, using the entities, entityTypes, and relationships arguments. All arguments will use the AND operator. For example, using the following arguments entities=8.8.8.8 entities_types=Domain will provide only relationships that the 8.8.8.8 indicator has with indicators of type domain.
python · Base
Source
import demistomock as demisto from CommonServerPython import * from CommonServerUserPython import * import re def filter_relationships_by_entity_types( entities: list, entities_types: list, relationships: list, limit: int, verbose: str = "false", revoked: str = "false" ) -> list: """ Filters indicator relationships by entity types using pagination. Args: entities: List of entities to search for relationships entities_types: List of entity types to filter by (EntityA or EntityB must match) relationships: List of relationship types to search for limit: Maximum number of filtered relationships to return Returns: list: Filtered relationships where EntityA or EntityB type matches entities_types """ filtered_relationships: list = [] searchAfter = None iteration = 0 while len(filtered_relationships) < limit: search_params = { "entities": entities, "relationships": relationships, "revoked": revoked, "verbose": verbose, } if searchAfter: search_params["searchAfter"] = searchAfter res = demisto.executeCommand("SearchIndicatorRelationships", search_params) if not res or len(res) == 0: demisto.info("No response received from SearchIndicatorRelationships") break data = demisto.get(res[0], "Contents") or {} demisto.info(f"{iteration=}, Received data: {data}") iteration += 1 if not data or data.get("Relationships") is None: demisto.info(f"Invalid or empty data received: {data}") break for relationship in data.get("Relationships", []): # For this case: # !SearchIndicatorRelationships Entities = google.com, example.com relationships=relates-to entity_type=DOMAIN # Relationships = google.com relates-to example.com if ( relationship["EntityAType"] in entities_types and relationship["EntityBType"] in entities_types and relationship["EntityA"] in entities and relationship["EntityB"] in entities ): filtered_relationships.append(relationship) elif (relationship["EntityAType"] in entities_types and relationship["EntityA"] not in entities) or ( relationship["EntityBType"] in entities_types and relationship["EntityB"] not in entities ): filtered_relationships.append(relationship) if len(filtered_relationships) >= limit: break searchAfter_list = data.get("RelationshipsPagination", []) searchAfter = searchAfter_list[0] if searchAfter_list else None demisto.info(f"SearchAfter updated: {searchAfter}") if not searchAfter or len(filtered_relationships) >= limit: break demisto.info(f"{filtered_relationships=}") return filtered_relationships def get_relationships(args: dict) -> list: """ Retrieves indicator relationships based on specified entities, entity types, and relationships. Args: entities (Optional[list[str]]): List of entity values to search for relationships entities_types (Optional[list[str]]): List of entity types to filter relationships by relationships (Optional[list[str]]): List of relationship types to search for limit (int): Maximum number of relationships to return (default: 20) Returns: list: List of relationships matching the search criteria. If entities_types is provided, returns filtered relationships where EntityA or EntityB type matches entities_types. Returns empty list if no parameters are provided or no relationships found. """ entities_types = argToList(args.pop("related_entity_types", [])) entities = argToList(args.get("entities", [])) relationships = argToList(args.get("relationships", [])) verbose = args.get("verbose", "false") revoked = args.get("revoked", "false") limit = int(args.get("limit", "20")) if entities_types and entities: filtered_relationships = filter_relationships_by_entity_types( entities, entities_types, relationships, limit, verbose, revoked ) return filtered_relationships search_params = { "entities": entities, "entities_types": entities_types, "relationships": relationships, "limit": limit, "verbose": verbose, "revoked": revoked, } remove_nulls_from_dictionary(search_params) res = demisto.executeCommand("SearchIndicatorRelationships", search_params) if not res or len(res) == 0: return [] data = demisto.get(res[0], "Contents") or {} return data.get("Relationships", []) or [] def main(): try: args = demisto.args() relationships = get_relationships(args) hr = tableToMarkdown( "Relationships", relationships, headers=[ "EntityA", "EntityAType", "EntityB", "EntityBType", "Relationship", ], headerTransform=lambda header: re.sub(r"\B([A-Z])", r" \1", header), ) return_results( CommandResults( readable_output=hr, outputs_prefix="Relationships", outputs=relationships, outputs_key_field="ID", ) ) except Exception as ex: return_error(f"Failed to execute SearchIndicatorRelationshipsAgentix. Error: {str(ex)}") if __name__ in ("__main__", "__builtin__", "builtins"): # pragma: no cover main()