Source
from enum import Enum import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 REPLACE_KEYS = [ ("base_values_to_search", "base_additional_values"), ("base_fields_to_search", "base_additional_fields"), ("base_field_state", "base_additional_field_state"), ("base_field_match", "base_additional_field_match"), ] class SectionNotFound(Exception): pass class Operators(Enum): OR = "OR" AND = "AND" class MatchRule(Enum): EQUAL = "{} = '{}'" NOT_EQUAL = "{} != '{}'" ILIKE = "{} ILIKE '%{}%'" NOT_ILIKE = "{} NOT ILIKE '%{}%'" def fields_section( fields_list: list[str], values_list: list[str], operator: Operators = Operators.OR, match_rule: MatchRule = MatchRule.EQUAL ) -> str: condition_list: list[str] = [] for field in (x if " " not in x else f"'{x}'" for x in fields_list): for value in values_list: condition_list.append(match_rule.value.format(field, value)) return f"({f' {operator.value} '.join(condition_list)})" def complete_query(select_fields: str, combined_sections: str, time_frame: str) -> str: return f"select {select_fields} from events where {combined_sections} {time_frame}" def prepare_section(args: dict, section_prefix: str) -> dict: try: values_list = argToList(args[f"{section_prefix}_additional_values"]) except KeyError: raise SectionNotFound(section_prefix) fields_list = args.get(f"{section_prefix}_additional_fields") if args[f"{section_prefix}_additional_field_match"] == "partial": if args[f"{section_prefix}_additional_field_state"] == "include": match_rule = MatchRule.ILIKE else: match_rule = MatchRule.NOT_ILIKE fields_list = fields_list or ["UTF8(payload)"] else: if not fields_list: raise KeyError(f"{section_prefix}_additional_fields") if args[f"{section_prefix}_additional_field_state"] == "include": match_rule = MatchRule.EQUAL else: match_rule = MatchRule.NOT_EQUAL return {"values_list": values_list, "match_rule": match_rule, "fields_list": argToList(fields_list)} def prepare_args(args: dict) -> dict: for key in list(args): if not args[key]: args.pop(key) for original_key, new_key in REPLACE_KEYS: try: args[new_key] = args.pop(original_key) except KeyError: # ignore the key beacuse a part of them are not required and we already handeling the key errors in the main function pass return args def original_key_name(key_name) -> str: for original_key, new_key in REPLACE_KEYS: if key_name == new_key: return original_key return key_name def create_sections_str(args: dict[str, str], operator: Operators = Operators.AND) -> str: sections = [] for section_prefix in ["base", "first", "second"]: try: sections.append(fields_section(**prepare_section(args, section_prefix))) except SectionNotFound: if section_prefix == "base": raise DemistoException("base arguments not given correctly") return f" {operator.value} ".join(sections) def main(): try: args = prepare_args(demisto.args()) time_frame = args["time_frame"] select_fields = args["select_fields"] aql_string = complete_query( select_fields=select_fields, combined_sections=create_sections_str(args), time_frame=time_frame, ) return_results(CommandResults(readable_output=aql_string, outputs={"QRadarQuery": aql_string})) except KeyError as key_error: key_name = original_key_name(key_error.args[0]) return_error(f"Missing {key_name}.") except Exception as error: return_error(str(error), error) if __name__ in ("__main__", "__builtin__", "builtins"): main()
README
Build QRadar AQL Query. (Available from Cortex XSOAR 6.0.0).
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | Utility |
| Cortex XSOAR Version | 6.0.0 |
Inputs
| Argument Name | Description |
|---|---|
| base_values_to_search | The values of the first field to search. This can be a single value or a comma-separated list of values. For example admin1,admin2. |
| base_fields_to_search | The field names of the first field to search. This can be a single value or a comma-separated list of values. For example admin1,admin2. |
| base_field_state | The state of the second field to search, meaning whether the values in the field should be included or excluded. Valid options are include or exclude. |
| base_field_match | Whether the values of the second field should be an exact match or a partial match. Valid options are exact or partial. |
| select_fields | The list of fields to select within the AQL query. The default fields are DATEFORMAT(devicetime,’dd-MM-yyyy hh:mm’),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username |
| time_frame | Time frame as used in AQL examples can be LAST 7 DAYS START ‘2019-09-25 15:51’ STOP ‘2019-09-25 17:51’. For more examples, view IBM’s AQL documentation. |
| first_additional_values | The values of the second field to search. This can be a single value or a comma-separated list of values. For example admin1,admin2. |
| first_additional_fields | The field names of the second field to search. This can be a single value or a comma-separated list of values. For example admin1,admin2. |
| first_additional_field_state | The state of the second field to search, meaning whether the values in the field should be included or excluded. Valid options are include or exclude. |
| first_additional_field_match | Whether the values of the second field should be an exact match or a partial match. Valid options are exact or partial. |
| second_additional_values | The values of the third field to search. This can be a single value or a comma-separated list of values. For example admin1,admin2 |
| second_additional_fields | The field names of the third field to search. This can be a single value or a comma-separated list of values. For example username,user |
| second_additional_field_state | The state of the third field to search, meaning whether the values in the field should be included or excluded. Valid options are include or exclude. |
| second_additional_field_match | Whether the values of the third field should be an exact match or a partial match. Valid options are exact or partial. When choosing exact, the AQL query will use the = operator. When choosing partial, the AQL query will use ILIKE and add ‘%%’ to the values. |
Outputs
| Path | Description | Type |
|---|---|---|
| QRadarQuery | The resultant AQL query based on the inputs. | string |
Script Example Search for a hash where we dont know the field
!QRadarCreateAQLQuery base_field_match=partial base_values_to_search=2367666DB8DFF58982A74695760E3EF0ACEBD050
Context Example
{
"QRadarQuery": "select DATEFORMAT(devicetime,'dd-MM-yyyy hh:mm'),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username from events where (UTF8(payload) ILIKE '%2367666DB8DFF58982A74695760E3EF0ACEBD050%') LAST 1 HOURS"
}
Human Readable Output
select DATEFORMAT(devicetime,’dd-MM-yyyy hh:mm’),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username from events where (UTF8(payload) ILIKE ‘%2367666DB8DFF58982A74695760E3EF0ACEBD050%’) LAST 1 HOURS
Script Example Search for a hash in specific fields
!QRadarCreateAQLQuery base_field_match=exact base_values_to_search=2367666DB8DFF58982A74695760E3EF0ACEBD050 base_fields_to_search=sha1,sha1-hash
Context Example
{
"QRadarQuery": "select DATEFORMAT(devicetime,'dd-MM-yyyy hh:mm'),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username from events where (sha1 = '2367666DB8DFF58982A74695760E3EF0ACEBD050' OR sha1-hash = '2367666DB8DFF58982A74695760E3EF0ACEBD050') LAST 1 HOURS"
}
Human Readable Output
select DATEFORMAT(devicetime,’dd-MM-yyyy hh:mm’),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username from events where (sha1 = ‘2367666DB8DFF58982A74695760E3EF0ACEBD050’ OR sha1-hash = ‘2367666DB8DFF58982A74695760E3EF0ACEBD050’) LAST 1 HOURS
Script Example Search for user and hash
!QRadarCreateAQLQuery base_field_match=exact base_values_to_search=2367666DB8DFF58982A74695760E3EF0ACEBD050 base_fields_to_search=sha1,sha1-hash first_additional_fields=username first_additional_values=admin
Context Example
{
"QRadarQuery": "select DATEFORMAT(devicetime,'dd-MM-yyyy hh:mm'),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username from events where (sha1 = '2367666DB8DFF58982A74695760E3EF0ACEBD050' OR sha1-hash = '2367666DB8DFF58982A74695760E3EF0ACEBD050') AND (username = 'admin') LAST 1 HOURS"
}
Human Readable Output
select DATEFORMAT(devicetime,’dd-MM-yyyy hh:mm’),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username from events where (sha1 = ‘2367666DB8DFF58982A74695760E3EF0ACEBD050’ OR sha1-hash = ‘2367666DB8DFF58982A74695760E3EF0ACEBD050’) AND (username = ‘admin’) LAST 1 HOURS
Script Example Search for a hash in payload that doesnt contain admin
!QRadarCreateAQLQuery base_field_match=exact base_values_to_search=2367666DB8DFF58982A74695760E3EF0ACEBD050 base_fields_to_search=sha1 first_additional_field_state=exclude first_additional_field_match=partial first_additional_values=admin
Context Example
{
"QRadarQuery": "select DATEFORMAT(devicetime,'dd-MM-yyyy hh:mm'),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username from events where (sha1 = '2367666DB8DFF58982A74695760E3EF0ACEBD050') AND (UTF8(payload) NOT ILIKE '%admin%') LAST 1 HOURS"
}
Human Readable Output
select DATEFORMAT(devicetime,’dd-MM-yyyy hh:mm’),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username from events where (sha1 = ‘2367666DB8DFF58982A74695760E3EF0ACEBD050’) AND (UTF8(payload) NOT ILIKE ‘%admin%’) LAST 1 HOURS