GetIndicatorsByQuery
Gets a list of indicator objects and the associated indicator outputs that match the specified query and filters. The results are returned in a structured data file.
- Type
- python
- Pack
- Base
Source
import hashlib
from CommonServerPython import *
PAGE_SIZE = 500
RANDOM_UUID = str(demisto.args().get("addRandomSalt", "").encode("utf8"))
# Memo for key matching
CACHE = {} # type: ignore
def hash_value(simple_value):
if not isinstance(simple_value, str):
simple_value = str(simple_value)
if simple_value.lower() in ["none", "null"]:
return None
return hashlib.md5(simple_value.encode("utf8") + RANDOM_UUID.encode("utf8")).hexdigest() # nosec
def pattern_match(pattern, s):
regex = re.compile(pattern.replace("*", ".*"))
return re.match(regex, s) is not None
def is_key_match_fields_to_hash(key, fields_to_hash):
if key is None:
return False
if key in CACHE:
return CACHE[key]
for field in fields_to_hash:
if pattern_match(field, key):
CACHE[key] = True
return True
return False
def hash_multiple(value, fields_to_hash, to_hash=False):
if isinstance(value, list):
return [hash_multiple(x, fields_to_hash, to_hash) for x in value]
if isinstance(value, dict):
for k, v in value.items():
_hash = to_hash or is_key_match_fields_to_hash(k, fields_to_hash)
value[k] = hash_multiple(v, fields_to_hash, _hash)
return value
else:
try:
if isinstance(value, int | float | bool):
to_hash = False
if not isinstance(value, str):
value = str(value)
except Exception:
value = ""
if to_hash and value:
return hash_value(value)
else:
return value
def parse_ioc(ioc):
global fields_to_hash, unpopulate_fields, populate_fields
# flat
cf = ioc.pop("CustomFields", {}) or {}
ioc.update(cf)
new_ioc = {}
for k, v in ioc.items():
if v in ["0001-01-01T00:00:00Z"]:
continue
if populate_fields:
if k in populate_fields:
new_ioc[k] = v
else:
if unpopulate_fields:
if k not in unpopulate_fields:
new_ioc[k] = v
else:
new_ioc[k] = v
ioc = new_ioc
if fields_to_hash and is_key_match_fields_to_hash(k, fields_to_hash):
ioc = hash_multiple(ioc, fields_to_hash)
return ioc
def find_indicators_with_limit_loop(indicator_query: str, limit: int):
"""
Finds indicators using while loop with demisto.searchIndicators, and returns result and last page
"""
iocs: List[dict] = []
demisto.debug(f"Searching indicators with {indicator_query=} and {populate_fields=}.")
search_indicators = IndicatorsSearcher(
query=indicator_query,
limit=limit,
size=PAGE_SIZE,
filter_fields=",".join(populate_fields) if populate_fields else None,
)
for ioc_res in search_indicators:
fetched_iocs = ioc_res.get("iocs") or []
iocs.extend(fetched_iocs)
demisto.debug(f"Received {len(iocs)} results from server. Parsing.")
return [parse_ioc(x) for x in iocs]
def get_parsed_populated_fields(fields_to_parse: list[str]) -> frozenset | None:
"""Gets a list of fields to populate for an indicator and parse it according to specific requirements."""
# Due to using the always populated field and no ability to provide None value in the UI, we allow an explicit override.
if "ALL" in fields_to_parse:
demisto.debug("All fields are requested. populated_field argument is set to None.")
return None
new_fields = [field for field in fields_to_parse if field]
# Due to a server bug where API request an result non-matching names, we allow this "non existing" field.
if "RelatedIncCount" in new_fields:
new_fields.append("investigationsCount")
demisto.debug(f"User's fields to populate: {new_fields}.")
return frozenset(new_fields)
fields_to_hash, unpopulate_fields, populate_fields = [], [], [] # type: ignore
def main():
global fields_to_hash, unpopulate_fields, populate_fields
args = demisto.args()
fields_to_hash = frozenset([x for x in argToList(args.get("fieldsToHash", "")) if x]) # type: ignore
unpopulate_fields = frozenset([x for x in argToList(args.get("dontPopulateFields", "")) if x]) # type: ignore
populate_fields = get_parsed_populated_fields(argToList(args.get("populateFields", ""))) # type: ignore
limit = int(args.get("limit", PAGE_SIZE))
query = args.get("query", "")
offset = int(args.get("offset", 0))
indicators = find_indicators_with_limit_loop(query, limit + offset)[offset : offset + limit]
entry = fileResult("indicators.json", json.dumps(indicators).encode("utf8"))
entry["Contents"] = indicators
entry["ContentsFormat"] = formats["json"]
entry["HumanReadable"] = f"Fetched {len(indicators)} indicators successfully by the query: {query}"
return entry
if __name__ in ["__main__", "__builtin__", "builtins"]:
demisto.results(main())
README
Gets a list of indicator objects and the associated indicator outputs that match the specified query and filters. The results are returned in a structured data file.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | ml |
| Cortex XSOAR Version | 5.5.0 |
Inputs
| Argument Name | Description |
|---|---|
| query | The indicators query. |
| dontPopulateFields | A comma-separated list of fields in the object to ignore. |
| limit | The maximum number of indicators to fetch. |
| offset | The results offset page. Only change when the number of the results exceed the limit. |
| addRandomSalt | Salt for the hash function. |
| fieldsToHash | A comma-separated list of fields to hash. Supports wildcard “*”. |
| populateFields | A comma-separated list of fields in the object to poplulate. Defaults are id, score, and investigationIDs. |
Outputs
There are no outputs for this script.