QRadarCreateAQLQuery
Build QRadar AQL Query.
- Type
- python
- Pack
- QRadar
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