Skip to content

Commit 723b70c

Browse files
author
oleksandr.volha
committed
fixes
1 parent 0b0a8d9 commit 723b70c

File tree

5 files changed

+110
-113
lines changed

5 files changed

+110
-113
lines changed

uncoder-core/app/translator/platforms/base/aql/functions/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from app.translator.core.models.functions.sort import SortLimitFunction
2828
from app.translator.platforms.base.aql.const import TABLE_PATTERN
2929
from app.translator.platforms.base.aql.functions.const import func_aliases_ctx_var, AGGREGATION_FUNCTIONS_MAP
30-
from app.translator.platforms.base.aql.functions.custom_types.functions import AQLFunctionType
30+
from app.translator.platforms.base.aql.functions.const import AQLFunctionType
3131
from app.translator.platforms.base.aql.functions.manager import AQLFunctionsManager
3232

3333

uncoder-core/app/translator/platforms/base/aql/functions/const.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
from contextvars import ContextVar
22

33
from app.translator.core.custom_types.functions import FunctionType
4-
from app.translator.platforms.base.aql.functions import AQLFunctionType
54
from app.translator.tools.custom_enum import CustomEnum
65

76

7+
class AQLFunctionType(CustomEnum):
8+
lower: str = "LOWER"
9+
upper: str = "UPPER"
10+
min: str = "MIN"
11+
max: str = "MAX"
12+
sum: str = "SUM"
13+
avg: str = "AVG"
14+
count: str = "COUNT"
15+
distinct_count: str = "DISTINCTCOUNT"
16+
last: str = "LAST"
17+
fields: str = "fields"
18+
aggregation_data_parser: str = "aggregation_data_parser"
19+
20+
821
class AQLSortOrderType(CustomEnum):
922
asc: str = "ASC"
1023
desc: str = "DESC"

uncoder-core/app/translator/platforms/base/aql/functions/custom_types/__init__.py

Whitespace-only changes.

uncoder-core/app/translator/platforms/base/aql/functions/custom_types/functions.py

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 95 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,95 @@
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-
import re
19-
from typing import ClassVar, Optional, Union
20-
21-
from app.translator.core.custom_types.tokens import OperatorType
22-
from app.translator.core.custom_types.values import ValueType
23-
from app.translator.core.models.field import FieldValue, Keyword
24-
from app.translator.core.models.identifier import Identifier
25-
from app.translator.core.str_value_manager import StrValue
26-
from app.translator.core.tokenizer import QueryTokenizer
27-
from app.translator.platforms.base.aql.const import NUM_VALUE_PATTERN, SINGLE_QUOTES_VALUE_PATTERN, UTF8_PAYLOAD_PATTERN
28-
from app.translator.platforms.base.aql.str_value_manager import aql_str_value_manager
29-
from app.translator.tools.utils import get_match_group
30-
31-
32-
class AQLTokenizer(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-
def should_process_value_wildcards(operator: Optional[str]) -> bool:
58-
return operator and operator.lower() in ("like", "ilike")
59-
60-
def get_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)) is not None:
64-
return mapped_operator, StrValue(num_value, split_value=[num_value])
65-
66-
if (bool_value := get_match_group(match, group_name=ValueType.bool_value)) is not None:
67-
return mapped_operator, StrValue(bool_value, split_value=[bool_value])
68-
69-
if (s_q_value := get_match_group(match, group_name=ValueType.single_quotes_value)) is not None:
70-
if mapped_operator == OperatorType.REGEX:
71-
return mapped_operator, self.str_value_manager.from_re_str_to_container(s_q_value)
72-
73-
if self.should_process_value_wildcards(operator):
74-
return mapped_operator, self.str_value_manager.from_str_to_container(s_q_value)
75-
76-
return mapped_operator, self.str_value_manager.from_str_to_container(s_q_value)
77-
78-
return super().get_operator_and_value(match, mapped_operator, operator)
79-
80-
def escape_field_name(self, field_name: str) -> str:
81-
return field_name.replace('"', r"\"").replace(" ", r"\ ")
82-
83-
@staticmethod
84-
def create_field_value(field_name: str, operator: Identifier, value: Union[str, list]) -> FieldValue:
85-
field_name = field_name.strip('"')
86-
return FieldValue(source_name=field_name, operator=operator, value=value)
87-
88-
def search_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-
return keyword, query[pos:]
94-
95-
96-
aql_tokenizer = AQLTokenizer()
1+
from typing import Optional
2+
3+
from app.translator.core.mapping import DEFAULT_MAPPING_NAME, BasePlatformMappings, LogSourceSignature, SourceMapping
4+
5+
6+
class AQLLogSourceSignature(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_types or [])
16+
self.categories = set(categories or [])
17+
self.qids = set(qids or [])
18+
self.qid_event_categories = set(qid_event_categories or [])
19+
self._default_source = default_source or {}
20+
21+
def is_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) if devicetype else None
29+
category_match = set(category).issubset(self.categories) if category else None
30+
qid_match = set(qid).issubset(self.qids) if qid else None
31+
qid_event_category_match = (
32+
set(qideventcategory).issubset(self.qid_event_categories) if qideventcategory else None
33+
)
34+
return all(
35+
condition
36+
for condition in (device_type_match, category_match, qid_match, qid_event_category_match)
37+
if condition is not None
38+
)
39+
40+
def __str__(self) -> str:
41+
return self._default_source.get("table", "events")
42+
43+
@property
44+
def extra_condition(self) -> str:
45+
default_source = self._default_source
46+
return " AND ".join((f"{key}={value}" for key, value in default_source.items() if key != "table" and value))
47+
48+
49+
class AQLMappings(BasePlatformMappings):
50+
skip_load_default_mappings: bool = False
51+
extend_default_mapping_with_all_fields: bool = True
52+
53+
def prepare_log_source_signature(self, mapping: dict) -> AQLLogSourceSignature:
54+
log_source = mapping.get("log_source", {})
55+
default_log_source = mapping["default_log_source"]
56+
return AQLLogSourceSignature(
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+
def get_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+
for source_mapping in self._source_mappings.values():
74+
if source_mapping.source_id == DEFAULT_MAPPING_NAME:
75+
continue
76+
77+
log_source_signature: AQLLogSourceSignature = source_mapping.log_source_signature
78+
if log_source_signature.is_suitable(devicetype, category, qid, qideventcategory): # noqa: SIM102
79+
if source_mapping.fields_mapping.is_suitable(field_names):
80+
suitable_source_mappings.append(source_mapping)
81+
82+
if not suitable_source_mappings:
83+
for source_mapping in self._source_mappings.values():
84+
if source_mapping.source_id == DEFAULT_MAPPING_NAME:
85+
continue
86+
if source_mapping.fields_mapping.is_suitable(field_names):
87+
suitable_source_mappings.append(source_mapping)
88+
89+
if not suitable_source_mappings:
90+
suitable_source_mappings = [self._source_mappings[DEFAULT_MAPPING_NAME]]
91+
92+
return suitable_source_mappings
93+
94+
95+
aql_mappings = AQLMappings(platform_dir="qradar")

0 commit comments

Comments
 (0)