CreateNewIndicatorsOnly
Create indicators to the Threat Intel database only if they are not registered. All submitted indicators will be associated with the parent incident. When using the script with many indicators, or when the Threat Intel Management database is highly populated, this script may have low performance issue.
python · Common Scripts
Source
import time from typing import Any import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 STATUS_NEW = "new" STATUS_EXISTING = "existing" STATUS_UNAVAILABLE = "unavailable" KEY_CREATION_STATUS = "CreationStatus" KEY_VALUE = "value" MAX_FIND_INDICATOR_RETRIES = 10 SLEEP_TIME = 2 def find_existing_indicators_by_value(indicator_values: list[str]) -> dict[str, dict[str, Any]]: """ Searches for existing indicators by their values using a single batched query. Args: indicator_values: List of indicator values to search for. Returns: A case-insensitive dictionary mapping indicator values to their indicator objects. """ escaped_normalized_indicators = {indicator_value.replace('"', r"\"") for indicator_value in indicator_values} if not escaped_normalized_indicators: return {} quoted_values = " ".join(f'"{indicator_value}"' for indicator_value in escaped_normalized_indicators) query = f"value:({quoted_values})" demisto.debug(f"Searching for existing indicators with query: {query}") searcher = IndicatorsSearcher(query=query) existing_indicators_by_value: dict[str, dict[str, Any]] = {} for page_result in searcher: for indicator in page_result.get("iocs") or []: indicator_value = indicator.get(KEY_VALUE) if indicator_value: existing_indicators_by_value.setdefault(str(indicator_value).casefold(), indicator) demisto.debug(f"Found {len(existing_indicators_by_value)} existing indicators") return existing_indicators_by_value def associate_indicator_to_incident(indicator_value: Any) -> None: """ Associate an indicator to this incident. Raise an exception if an error occurs. """ incident_id = demisto.incidents()[0].get("id") demisto.debug(f"The incident id is {incident_id}") cmd_args = { "incidentId": incident_id, "value": f"{indicator_value}", # Force an error } retry_num = 1 res = "" while res != "done" and retry_num <= MAX_FIND_INDICATOR_RETRIES: try: demisto.debug(f"Executing associateIndicatorToIncident command with retry {retry_num}/{MAX_FIND_INDICATOR_RETRIES}") res = execute_command("associateIndicatorToIncident", cmd_args) demisto.debug(f"The associateIndicatorToIncident response is: {res}") except Exception as err: if "For associateIndicatorToIncident found no indicator" not in str(err): raise err # else log and continue with retries after sleeping demisto.debug(f"Failed to find indicator {indicator_value} in the system.") if retry_num != MAX_FIND_INDICATOR_RETRIES: time.sleep(SLEEP_TIME) # pylint: disable=E9003 retry_num += 1 if res != "done": raise Exception(f"Failed to associate {indicator_value} with incident {incident_id}") def normalize_indicator_value(indicator_value: Any) -> str: if isinstance(indicator_value, int): return str(indicator_value) elif isinstance(indicator_value, str) and indicator_value: return indicator_value else: raise DemistoException(f"Invalid indicator value: {indicator_value!s}") def add_new_indicator( indicator_value: Any, create_new_indicator_args: dict[str, Any], associate_to_incident: bool = False, existing_indicators_by_value: dict[str, dict[str, Any]] | None = None, ) -> dict[str, Any]: indicator_value = normalize_indicator_value(indicator_value) existing_indicators_by_value = existing_indicators_by_value or {} if indicator := existing_indicators_by_value.get(indicator_value.casefold()): indicator[KEY_CREATION_STATUS] = STATUS_EXISTING # findIndicators might find an indicator with different letter case. # Unfortunately associate_indicator_to_incident does not ignore case # and as a result in some cases is unable to associate indicator. indicator_value = indicator[KEY_VALUE] else: args = dict(create_new_indicator_args, value=indicator_value) indicator = execute_command("createNewIndicator", args) if isinstance(indicator, dict): indicator[KEY_CREATION_STATUS] = STATUS_NEW elif isinstance(indicator, str): # createNewIndicator has been successfully done, but the indicator # wasn't created for some reasons. if "done - Indicator was not created" in indicator: demisto.debug(f'Indicator was not created. Make sure "{indicator_value}" is not excluded.') else: demisto.debug(indicator) indicator = { "value": indicator_value, "indicator_type": args.get("type", "Unknown"), KEY_CREATION_STATUS: STATUS_UNAVAILABLE, } else: raise DemistoException(f"Unknown response from createNewIndicator: {indicator!r}") if indicator[KEY_CREATION_STATUS] != STATUS_UNAVAILABLE and associate_to_incident: demisto.debug(f"Associating {indicator_value} to incident.") associate_indicator_to_incident(indicator_value) return indicator def add_new_indicators( indicator_values: list[Any] | None, create_new_indicator_args: dict[str, Any], associate_to_incident: bool = False, ) -> list[dict[str, Any]]: normalized_indicator_values = [normalize_indicator_value(indicator_value) for indicator_value in indicator_values or []] existing_indicators_by_value = find_existing_indicators_by_value(normalized_indicator_values) return [ add_new_indicator( indicator_value, create_new_indicator_args, associate_to_incident, existing_indicators_by_value, ) for indicator_value in normalized_indicator_values ] def main(): try: args = assign_params(**demisto.args()) # Don't use argToList to make a list in order to accept an indicator including commas. # The `indicator_values` parameter doesn't support a comma separated list. if (indicator_values := args.get("indicator_values", [])) and not isinstance(indicator_values, list): indicator_values = [indicator_values] create_new_indicator_args = dict(args) create_new_indicator_args.pop("indicator_values", None) create_new_indicator_args.pop("verbose", None) associate_to_incident = argToBoolean(create_new_indicator_args.pop("associate_to_current", "false")) ents = add_new_indicators(indicator_values, create_new_indicator_args, associate_to_incident) outputs = [ assign_params( ID=ent.get("id"), Score=ent.get("score"), CreationStatus=ent.get(KEY_CREATION_STATUS), Type=ent.get("indicator_type"), Value=ent.get("value"), ) for ent in ents ] count_new = sum(1 for ent in ents if ent.get(KEY_CREATION_STATUS) == STATUS_NEW) readable_output = f"{count_new} new indicators have been added." if argToBoolean(args.get("verbose", "false")): readable_output += "\n" + tblToMd( "New Indicator Created", outputs, ["ID", "Score", "CreationStatus", "Type", "Value"] ) return_results( CommandResults( outputs_prefix="CreateNewIndicatorsOnly", outputs_key_field=["Value", "Type"], outputs=outputs, raw_response=ents, readable_output=readable_output, ) ) except Exception as e: return_error(f"Failed to execute CreateNewIndicatorsOnly.\nError:\n{e!s}") if __name__ in ("__main__", "__builtin__", "builtins"): main()
README
Create indicators to the Threat Intel database only if they are not registered. When using the script with many indicators, or when the Threat Intel Management database is highly populated, this script may have low performance issue.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | Utility |
| Cortex XSOAR Version | 6.5.0 |
Inputs
| Argument Name | Description |
|---|---|
| indicator_values | The indicator values |
| type | The indicator type of the indicators. |
| source | The indicator source. |
| verdict | The indicator reputation. |
| tags | The tags to add to the new indicators. |
| verbose | Whether to output additional messages as readable output. |
Outputs
| Path | Description | Type |
|---|---|---|
| CreateNewIndicatorsOnly.ID | The indicator ID. | string |
| CreateNewIndicatorsOnly.Value | The indicator value. | string |
| CreateNewIndicatorsOnly.Type | The indicator type. | string |
| CreateNewIndicatorsOnly.Score | The indicator score. | number |
| CreateNewIndicatorsOnly.CreationStatus | The status of the indicator requested to add. Value is either new, existing, or unavailable. | string |
Notice
When using the script with many indicators, or when the Threat Intel Management database is highly populated, this script may have low performance issue.