|
1 | | -""" |
2 | | -Uncoder IO Commercial Edition License |
3 | | ------------------------------------------------------------------ |
4 | | -Copyright (c) 2024 SOC Prime, Inc. |
5 | | -
|
6 | | -This file is part of the Uncoder IO Commercial Edition ("CE") and is |
7 | | -licensed under the Uncoder IO Non-Commercial License (the "License"); |
8 | | -you may not use this file except in compliance with the License. |
9 | | -You may obtain a copy of the License at |
10 | | -
|
11 | | - https://github.com/UncoderIO/UncoderIO/blob/main/LICENSE |
12 | | -
|
13 | | -Unless required by applicable law or agreed to in writing, software |
14 | | -distributed under the License is distributed on an "AS IS" BASIS, |
15 | | -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | ------------------------------------------------------------------ |
17 | | -""" |
18 | | -importre |
19 | | -fromtypingimportClassVar,Optional,Union |
20 | | - |
21 | | -fromapp.translator.core.custom_types.tokensimportOperatorType |
22 | | -fromapp.translator.core.custom_types.valuesimportValueType |
23 | | -fromapp.translator.core.models.fieldimportFieldValue,Keyword |
24 | | -fromapp.translator.core.models.identifierimportIdentifier |
25 | | -fromapp.translator.core.str_value_managerimportStrValue |
26 | | -fromapp.translator.core.tokenizerimportQueryTokenizer |
27 | | -fromapp.translator.platforms.base.aql.constimportNUM_VALUE_PATTERN,SINGLE_QUOTES_VALUE_PATTERN,UTF8_PAYLOAD_PATTERN |
28 | | -fromapp.translator.platforms.base.aql.str_value_managerimportaql_str_value_manager |
29 | | -fromapp.translator.tools.utilsimportget_match_group |
30 | | - |
31 | | - |
32 | | -classAQLTokenizer(QueryTokenizer): |
33 | | -single_value_operators_map:ClassVar[dict[str,str]]= { |
34 | | -"=":OperatorType.EQ, |
35 | | -"<=":OperatorType.LTE, |
36 | | -"<":OperatorType.LT, |
37 | | -">=":OperatorType.GTE, |
38 | | -">":OperatorType.GT, |
39 | | -"!=":OperatorType.NOT_EQ, |
40 | | -"like":OperatorType.EQ, |
41 | | -"ilike":OperatorType.EQ, |
42 | | -"matches":OperatorType.REGEX, |
43 | | -"imatches":OperatorType.REGEX, |
44 | | - } |
45 | | -multi_value_operators_map:ClassVar[dict[str,str]]= {"in":OperatorType.EQ} |
46 | | - |
47 | | -field_pattern=r'(?P<field_name>"[a-zA-Z\._\-\s]+"|[a-zA-Z\._\-]+)' |
48 | | -bool_value_pattern=rf"(?P<{ValueType.bool_value}>true|false)\s*" |
49 | | -_value_pattern=rf"{NUM_VALUE_PATTERN}|{bool_value_pattern}|{SINGLE_QUOTES_VALUE_PATTERN}" |
50 | | -multi_value_pattern=rf"""\((?P<{ValueType.multi_value}>[:a-zA-Z\"\*0-9=+%#\-_\/\\'\,.&^@!\(\s]*)\)""" |
51 | | -keyword_pattern=rf"{UTF8_PAYLOAD_PATTERN}\s+(?:like|LIKE|ilike|ILIKE)\s+{SINGLE_QUOTES_VALUE_PATTERN}" |
52 | | - |
53 | | -wildcard_symbol="%" |
54 | | -str_value_manager=aql_str_value_manager |
55 | | - |
56 | | -@staticmethod |
57 | | -defshould_process_value_wildcards(operator:Optional[str])->bool: |
58 | | -returnoperatorandoperator.lower()in ("like","ilike") |
59 | | - |
60 | | -defget_operator_and_value( |
61 | | -self,match:re.Match,mapped_operator:str=OperatorType.EQ,operator:Optional[str]=None |
62 | | - )->tuple[str,StrValue]: |
63 | | -if (num_value:=get_match_group(match,group_name=ValueType.number_value))isnotNone: |
64 | | -returnmapped_operator,StrValue(num_value,split_value=[num_value]) |
65 | | - |
66 | | -if (bool_value:=get_match_group(match,group_name=ValueType.bool_value))isnotNone: |
67 | | -returnmapped_operator,StrValue(bool_value,split_value=[bool_value]) |
68 | | - |
69 | | -if (s_q_value:=get_match_group(match,group_name=ValueType.single_quotes_value))isnotNone: |
70 | | -ifmapped_operator==OperatorType.REGEX: |
71 | | -returnmapped_operator,self.str_value_manager.from_re_str_to_container(s_q_value) |
72 | | - |
73 | | -ifself.should_process_value_wildcards(operator): |
74 | | -returnmapped_operator,self.str_value_manager.from_str_to_container(s_q_value) |
75 | | - |
76 | | -returnmapped_operator,self.str_value_manager.from_str_to_container(s_q_value) |
77 | | - |
78 | | -returnsuper().get_operator_and_value(match,mapped_operator,operator) |
79 | | - |
80 | | -defescape_field_name(self,field_name:str)->str: |
81 | | -returnfield_name.replace('"',r"\"").replace(" ",r"\ ") |
82 | | - |
83 | | -@staticmethod |
84 | | -defcreate_field_value(field_name:str,operator:Identifier,value:Union[str,list])->FieldValue: |
85 | | -field_name=field_name.strip('"') |
86 | | -returnFieldValue(source_name=field_name,operator=operator,value=value) |
87 | | - |
88 | | -defsearch_keyword(self,query:str)->tuple[Keyword,str]: |
89 | | -keyword_search=re.search(self.keyword_pattern,query) |
90 | | -_,value=self.get_operator_and_value(keyword_search) |
91 | | -keyword=Keyword(value=value.strip(self.wildcard_symbol)) |
92 | | -pos=keyword_search.end() |
93 | | -returnkeyword,query[pos:] |
94 | | - |
95 | | - |
96 | | -aql_tokenizer=AQLTokenizer() |
| 1 | +fromtypingimportOptional |
| 2 | + |
| 3 | +fromapp.translator.core.mappingimportDEFAULT_MAPPING_NAME,BasePlatformMappings,LogSourceSignature,SourceMapping |
| 4 | + |
| 5 | + |
| 6 | +classAQLLogSourceSignature(LogSourceSignature): |
| 7 | +def__init__( |
| 8 | +self, |
| 9 | +device_types:Optional[list[int]], |
| 10 | +categories:Optional[list[int]], |
| 11 | +qids:Optional[list[int]], |
| 12 | +qid_event_categories:Optional[list[int]], |
| 13 | +default_source:dict, |
| 14 | + ): |
| 15 | +self.device_types=set(device_typesor []) |
| 16 | +self.categories=set(categoriesor []) |
| 17 | +self.qids=set(qidsor []) |
| 18 | +self.qid_event_categories=set(qid_event_categoriesor []) |
| 19 | +self._default_source=default_sourceor {} |
| 20 | + |
| 21 | +defis_suitable( |
| 22 | +self, |
| 23 | +devicetype:Optional[list[int]], |
| 24 | +category:Optional[list[int]], |
| 25 | +qid:Optional[list[int]], |
| 26 | +qideventcategory:Optional[list[int]], |
| 27 | + )->bool: |
| 28 | +device_type_match=set(devicetype).issubset(self.device_types)ifdevicetypeelseNone |
| 29 | +category_match=set(category).issubset(self.categories)ifcategoryelseNone |
| 30 | +qid_match=set(qid).issubset(self.qids)ifqidelseNone |
| 31 | +qid_event_category_match= ( |
| 32 | +set(qideventcategory).issubset(self.qid_event_categories)ifqideventcategoryelseNone |
| 33 | + ) |
| 34 | +returnall( |
| 35 | +condition |
| 36 | +forconditionin (device_type_match,category_match,qid_match,qid_event_category_match) |
| 37 | +ifconditionisnotNone |
| 38 | + ) |
| 39 | + |
| 40 | +def__str__(self)->str: |
| 41 | +returnself._default_source.get("table","events") |
| 42 | + |
| 43 | +@property |
| 44 | +defextra_condition(self)->str: |
| 45 | +default_source=self._default_source |
| 46 | +return" AND ".join((f"{key}={value}"forkey,valueindefault_source.items()ifkey!="table"andvalue)) |
| 47 | + |
| 48 | + |
| 49 | +classAQLMappings(BasePlatformMappings): |
| 50 | +skip_load_default_mappings:bool=False |
| 51 | +extend_default_mapping_with_all_fields:bool=True |
| 52 | + |
| 53 | +defprepare_log_source_signature(self,mapping:dict)->AQLLogSourceSignature: |
| 54 | +log_source=mapping.get("log_source", {}) |
| 55 | +default_log_source=mapping["default_log_source"] |
| 56 | +returnAQLLogSourceSignature( |
| 57 | +device_types=log_source.get("devicetype"), |
| 58 | +categories=log_source.get("category"), |
| 59 | +qids=log_source.get("qid"), |
| 60 | +qid_event_categories=log_source.get("qideventcategory"), |
| 61 | +default_source=default_log_source, |
| 62 | + ) |
| 63 | + |
| 64 | +defget_suitable_source_mappings( |
| 65 | +self, |
| 66 | +field_names:list[str], |
| 67 | +devicetype:Optional[list[int]]=None, |
| 68 | +category:Optional[list[int]]=None, |
| 69 | +qid:Optional[list[int]]=None, |
| 70 | +qideventcategory:Optional[list[int]]=None, |
| 71 | + )->list[SourceMapping]: |
| 72 | +suitable_source_mappings= [] |
| 73 | +forsource_mappinginself._source_mappings.values(): |
| 74 | +ifsource_mapping.source_id==DEFAULT_MAPPING_NAME: |
| 75 | +continue |
| 76 | + |
| 77 | +log_source_signature:AQLLogSourceSignature=source_mapping.log_source_signature |
| 78 | +iflog_source_signature.is_suitable(devicetype,category,qid,qideventcategory):# noqa: SIM102 |
| 79 | +ifsource_mapping.fields_mapping.is_suitable(field_names): |
| 80 | +suitable_source_mappings.append(source_mapping) |
| 81 | + |
| 82 | +ifnotsuitable_source_mappings: |
| 83 | +forsource_mappinginself._source_mappings.values(): |
| 84 | +ifsource_mapping.source_id==DEFAULT_MAPPING_NAME: |
| 85 | +continue |
| 86 | +ifsource_mapping.fields_mapping.is_suitable(field_names): |
| 87 | +suitable_source_mappings.append(source_mapping) |
| 88 | + |
| 89 | +ifnotsuitable_source_mappings: |
| 90 | +suitable_source_mappings= [self._source_mappings[DEFAULT_MAPPING_NAME]] |
| 91 | + |
| 92 | +returnsuitable_source_mappings |
| 93 | + |
| 94 | + |
| 95 | +aql_mappings=AQLMappings(platform_dir="qradar") |