import json
import demistomock as demisto # noqa: F401
from CommonServerPython import * # noqa: F401
EQ = "EQ"
OUTPUT_KEYS = [
"internal_id",
"severity",
"Identity_type",
"issue_name",
"issue_source",
"case_ids",
"agent_id",
"actor_process_image_sha256",
"causality_actor_process_image_sha256",
"action_process_image_sha256",
"issue_category",
"issue_domain",
"issue_description",
"os_actor_process_image_sha256",
"action_file_macro_sha256",
"status.progress",
"asset_ids",
"assigned_to_pretty",
"assigned_to",
"source_insert_ts",
]
SEARCH_SHA256_FIELDS = [
"actor_process_image_sha256",
"causality_actor_process_image_sha256",
"action_process_image_sha256",
"os_actor_process_image_sha256",
"action_file_macro_sha256",
]
NUMERIC_ARGS = {"page", "page_size"}
NUMERIC_LIST_ARGS = {"issue_id"}
def remove_empty_string_values(args: dict) -> dict:
"""Remove empty/invalid values from the args dictionary.
- Removes keys with empty string values.
- For numeric args, removes non-numeric values to prevent be3 crashes (e.g. 'n/a', 'invalid_offset').
"""
def is_valid(key, value):
if value == "":
return False
if key in NUMERIC_ARGS:
return str(value).strip().isdigit()
if key in NUMERIC_LIST_ARGS:
parts = argToList(value)
return bool(parts) and all(str(part).strip().isdigit() for part in parts)
return True
return {key: value for key, value in args.items() if is_valid(key, value)}
def create_sha_search_field_query(sha_search_field: str, search_type: str, sha_list: list[str]) -> Optional[dict]:
"""
Given a list of sha256 values, builds a query of this form: { "AND": [ { {"OR": [{"SEARCH_FIELD": sha_search_field,
"SEARCH_TYPE": search_type ,"SEARCH_VALUE": sha_list[0]} , .... , {"SEARCH_FIELD": sha_search_field,"SEARCH_TYPE":
search_type ,"SEARCH_VALUE": sha_list[-1]} ]} } ] }
"""
if not sha_list:
return None
or_operator_list = []
for sha in sha_list:
or_operator_list.append({"SEARCH_FIELD": sha_search_field, "SEARCH_TYPE": search_type, "SEARCH_VALUE": sha})
return {"AND": [{"OR": or_operator_list}]}
def prepare_sha256_custom_field(args: dict) -> Optional[str]:
"""
Builds a structured query from a list of SHA256 values and assigns it to the 'custom_filter' field in the given args.
The function:
- Extracts the 'sha256' argument (as a string or list).
- For each predefined SHA256 search field, constructs a query block:
- Uses 'EQ'
- Each SHA is mapped to an OR clause per field.
- Combines all field-specific queries under a top-level OR.
- Adds the final query as a JSON string to args['custom_filter'].
Example structure added to args["custom_filter"]:
{
"OR": [
{
"AND": [
{
"OR": [
{
"SEARCH_FIELD": "actor_process_image_sha256",
"SEARCH_TYPE": "EQ",
"SEARCH_VALUE": "abc"
}
]
}
]
},
{
"AND": [
{
"OR": [
{
"SEARCH_FIELD": "causality_actor_process_image_sha256",
"SEARCH_TYPE": "EQ",
"SEARCH_VALUE": "xyz"
}
]
}
]
}
]
}
"""
sha256 = argToList(args.pop("sha256", ""))
if not sha256:
return None
or_operator_list: list[dict] = []
for sha_search_field in SEARCH_SHA256_FIELDS:
sha_search_field_query = create_sha_search_field_query(sha_search_field, EQ, sha256)
if sha_search_field_query:
or_operator_list.append(sha_search_field_query)
return json.dumps({"OR": or_operator_list})
def main(): # pragma: no cover
try:
args: dict = demisto.args()
# Return only specific fields to the context.
args["output_keys"] = ",".join(OUTPUT_KEYS)
sha256_custom_field = prepare_sha256_custom_field(args)
if sha256_custom_field:
args["custom_filter"] = sha256_custom_field
if issue_domain := args.get("issue_domain"):
args["issue_domain"] = f"DOMAIN_{issue_domain.upper().replace(' ', '_')}"
args = remove_empty_string_values(args)
demisto.debug(f"Calling core-get-issues with arguments: {args}")
results = demisto.executeCommand("core-get-issues", args)[0] # type: ignore
if is_error(results):
error = get_error(results)
demisto.debug("error: " + error)
raise DemistoException(f"Failed to execute the core-get-issues command {error}")
context = results.get("EntryContext", {}).get("Core.Issue(val.internal_id && val.internal_id == obj.internal_id)")
human_readable: str = results.get("HumanReadable", "")
for item in context or []:
if "agent_id" in item:
item["endpoint_id"] = item.pop("agent_id")
return_results(
CommandResults(
outputs=context,
outputs_prefix="Core.Issue",
outputs_key_field="internal_id",
readable_output=human_readable,
)
)
except DemistoException as error:
return_error(f"Failed to execute SearchIssues. Error:\n{error}", error)
if __name__ in ("__main__", "__builtin__", "builtins"): # pragma: no cover
main()