-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of github.com:demisto/content into MSDE_PB_Isol…
…ate_Unisolate_device
- Loading branch information
Showing
6 changed files
with
549 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
#### Scripts | ||
##### New: QRadarCreateAQLQuery | ||
- Build QRadar AQL Query. (Available from Cortex XSOAR 6.0.0). |
123 changes: 123 additions & 0 deletions
123
Packs/QRadar/Scripts/QRadarCreateAQLQuery/QRadarCreateAQLQuery.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import demistomock as demisto # noqa: F401 | ||
from CommonServerPython import * # noqa: F401 | ||
from enum import Enum | ||
from typing import Dict, List | ||
|
||
|
||
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 map(lambda x: x if ' ' not in x else f"'{x}'", 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() |
107 changes: 107 additions & 0 deletions
107
Packs/QRadar/Scripts/QRadarCreateAQLQuery/QRadarCreateAQLQuery.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
commonfields: | ||
id: QRadarCreateAQLQuery | ||
version: -1 | ||
name: QRadarCreateAQLQuery | ||
script: '' | ||
type: python | ||
tags: | ||
- Utility | ||
comment: Build QRadar AQL Query. | ||
args: | ||
- name: base_values_to_search | ||
required: true | ||
isArray: true | ||
description: 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. | ||
- name: base_fields_to_search | ||
isArray: true | ||
description: 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. | ||
- name: base_field_state | ||
required: true | ||
auto: PREDEFINED | ||
predefined: | ||
- include | ||
- exclude | ||
defaultValue: include | ||
description: The state of the first field to search, meaning whether the values in | ||
the field should be included or excluded. | ||
- name: base_field_match | ||
required: true | ||
auto: PREDEFINED | ||
predefined: | ||
- exact | ||
- partial | ||
defaultValue: exact | ||
description: Whether the values of the second field should be an exact match or a partial | ||
match. | ||
- name: select_fields | ||
required: true | ||
description: The list of fields to select within the AQL query. | ||
defaultValue: DATEFORMAT(devicetime,'dd-MM-yyyy hh:mm'),LOGSOURCENAME(logsourceid),CATEGORYNAME(category),QIDNAME(qid),sourceip,destinationip,username | ||
- name: time_frame | ||
required: true | ||
description: 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. | ||
defaultValue: LAST 1 HOURS | ||
- name: first_additional_values | ||
description: 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. | ||
isArray: true | ||
- name: first_additional_fields | ||
description: 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. | ||
isArray: true | ||
- name: first_additional_field_state | ||
required: true | ||
auto: PREDEFINED | ||
predefined: | ||
- include | ||
- exclude | ||
description: The state of the second field to search, meaning whether the values in | ||
the field should be included or excluded. | ||
defaultValue: include | ||
- name: first_additional_field_match | ||
required: true | ||
auto: PREDEFINED | ||
predefined: | ||
- exact | ||
- partial | ||
description: Whether the values of the second field should be an exact match or a partial | ||
match. | ||
defaultValue: exact | ||
- name: second_additional_values | ||
description: 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. | ||
- name: second_additional_fields | ||
description: 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. | ||
- name: second_additional_field_state | ||
required: true | ||
auto: PREDEFINED | ||
predefined: | ||
- include | ||
- exclude | ||
description: The state of the third field to search, meaning whether the values in the | ||
field should be included or excluded. | ||
defaultValue: include | ||
- name: second_additional_field_match | ||
auto: PREDEFINED | ||
predefined: | ||
- exact | ||
- partial | ||
description: Whether the values of the third field should be an exact match or a partial | ||
match. When choosing exact, the AQL query | ||
will use the = operator. When choosing partial, the AQL query will use ILIKE and add '%%' | ||
to the values. | ||
defaultValue: exact | ||
outputs: | ||
- contextPath: QRadarQuery | ||
description: The resultant AQL query based on the inputs. | ||
type: string | ||
subtype: python3 | ||
dockerimage: demisto/python3:3.9.8.24399 | ||
runas: DBotWeakRole | ||
fromversion: 6.0.0 | ||
tests: | ||
- No tests (auto formatted) |
Oops, something went wrong.