From 26402af07f648a26dca87a45c37bc8aaab6d9d1d Mon Sep 17 00:00:00 2001 From: Gesyk Nazar <77268518+nazargesyk@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:25:54 +0200 Subject: [PATCH 1/2] gis-9195 fix sentinel one power query regex escaping --- .../platforms/sentinel_one/escape_manager.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 uncoder-core/app/translator/platforms/sentinel_one/escape_manager.py diff --git a/uncoder-core/app/translator/platforms/sentinel_one/escape_manager.py b/uncoder-core/app/translator/platforms/sentinel_one/escape_manager.py new file mode 100644 index 00000000..4beb7c23 --- /dev/null +++ b/uncoder-core/app/translator/platforms/sentinel_one/escape_manager.py @@ -0,0 +1,17 @@ +from typing import ClassVar + +from app.translator.core.custom_types.values import ValueType +from app.translator.core.escape_manager import EscapeManager +from app.translator.core.models.escape_details import EscapeDetails +from app.translator.platforms.sentinel_one.custom_types.values import SentinelOneValueType + + +class SentinelOnePowerQueryEscapeManager(EscapeManager): + escape_map: ClassVar[dict[str, list[EscapeDetails]]] = { + ValueType.value: [EscapeDetails(pattern=r"\\", escape_symbols=r"\\\\")], + ValueType.regex_value: [EscapeDetails(pattern=r"([$^*+()\[\]{}|.?\-\\])", escape_symbols=r"\\\\\\\\")], + SentinelOneValueType.double_escape_regex_value: [EscapeDetails(pattern=r"\\", escape_symbols=r"\\\\")], + } + + +sentinel_one_power_query_escape_manager = SentinelOnePowerQueryEscapeManager() From 26e468c7eb3c0dd95f71997b8c906ecee37ab657 Mon Sep 17 00:00:00 2001 From: Gesyk Nazar <77268518+nazargesyk@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:44:56 +0200 Subject: [PATCH 2/2] gis-9195 fixes --- .../renders/microsoft_sentinel_rule.py | 12 +- .../platforms/sentinel_one/escape_manager.py | 2 +- .../renders/sentinel_one_power_query.py | 105 ++++++++++++++++++ 3 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 uncoder-core/app/translator/platforms/sentinel_one/renders/sentinel_one_power_query.py diff --git a/uncoder-core/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py b/uncoder-core/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py index 11722f89..3fa75c63 100644 --- a/uncoder-core/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py +++ b/uncoder-core/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py @@ -26,7 +26,7 @@ from app.translator.core.custom_types.meta_info import SeverityType from app.translator.core.mapping import SourceMapping from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.models.query_container import MetaInfoContainer, MitreInfoContainer +from app.translator.core.models.query_container import MetaInfoContainer, MitreInfoContainer, RawQueryContainer from app.translator.managers import render_manager from app.translator.platforms.microsoft.const import DEFAULT_MICROSOFT_SENTINEL_RULE, microsoft_sentinel_rule_details from app.translator.platforms.microsoft.mapping import MicrosoftSentinelMappings, microsoft_sentinel_rule_mappings @@ -105,9 +105,10 @@ def finalize_query( not_supported_functions: Optional[list] = None, unmapped_fields: Optional[list[str]] = None, *args, # noqa: ARG002 - **kwargs, # noqa: ARG002 + **kwargs, ) -> str: - query = super().finalize_query(prefix=prefix, query=query, functions=functions) + if not kwargs.get("raw_query", False): + query = super().finalize_query(prefix=prefix, query=query, functions=functions) rule = copy.deepcopy(DEFAULT_MICROSOFT_SENTINEL_RULE) rule["query"] = query rule["displayName"] = meta_info.title or _AUTOGENERATED_TEMPLATE @@ -130,3 +131,8 @@ def finalize_query( json_rule = json.dumps(rule, indent=4, sort_keys=False) json_rule = self.wrap_with_unmapped_fields(json_rule, unmapped_fields) return self.wrap_with_not_supported_functions(json_rule, not_supported_functions) + + def generate_from_raw_query_container(self, query_container: RawQueryContainer) -> str: + return self.finalize_query( + prefix="", query=query_container.query, functions="", meta_info=query_container.meta_info, raw_query=True + ) diff --git a/uncoder-core/app/translator/platforms/sentinel_one/escape_manager.py b/uncoder-core/app/translator/platforms/sentinel_one/escape_manager.py index 4beb7c23..04193dce 100644 --- a/uncoder-core/app/translator/platforms/sentinel_one/escape_manager.py +++ b/uncoder-core/app/translator/platforms/sentinel_one/escape_manager.py @@ -9,7 +9,7 @@ class SentinelOnePowerQueryEscapeManager(EscapeManager): escape_map: ClassVar[dict[str, list[EscapeDetails]]] = { ValueType.value: [EscapeDetails(pattern=r"\\", escape_symbols=r"\\\\")], - ValueType.regex_value: [EscapeDetails(pattern=r"([$^*+()\[\]{}|.?\-\\])", escape_symbols=r"\\\\\\\\")], + ValueType.regex_value: [EscapeDetails(pattern=r"([$^*+()\[\]{}|.?\-\\])", escape_symbols=r"\\\1")], SentinelOneValueType.double_escape_regex_value: [EscapeDetails(pattern=r"\\", escape_symbols=r"\\\\")], } diff --git a/uncoder-core/app/translator/platforms/sentinel_one/renders/sentinel_one_power_query.py b/uncoder-core/app/translator/platforms/sentinel_one/renders/sentinel_one_power_query.py new file mode 100644 index 00000000..28752105 --- /dev/null +++ b/uncoder-core/app/translator/platforms/sentinel_one/renders/sentinel_one_power_query.py @@ -0,0 +1,105 @@ +from typing import Union + +from app.translator.const import DEFAULT_VALUE_TYPE +from app.translator.core.custom_types.values import ValueType +from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.render import BaseFieldValueRender, PlatformQueryRender +from app.translator.core.str_value_manager import StrValueManager +from app.translator.managers import render_manager +from app.translator.platforms.sentinel_one.const import sentinel_one_power_query_details +from app.translator.platforms.sentinel_one.mapping import ( + SentinelOnePowerQueryMappings, + sentinel_one_power_query_query_mappings, +) +from app.translator.platforms.sentinel_one.str_value_manager import sentinel_one_power_query_str_value_manager + + +class SentinelOnePowerQueryFieldValue(BaseFieldValueRender): + details: PlatformDetails = sentinel_one_power_query_details + str_value_manager: StrValueManager = sentinel_one_power_query_str_value_manager + list_token = ", " + + @staticmethod + def _wrap_str_value(value: str) -> str: + return f'"{value}"' + + def equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: + if isinstance(value, list): + values = self.list_token.join( + self._pre_process_value(field, v, value_type=ValueType.value, wrap_str=True) for v in value + ) + return f"{field} in ({values})" + value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True) + return f"{field} = {value}" + + def less_modifier(self, field: str, value: Union[int, str]) -> str: + value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True) + return f"{field} < {value}" + + def less_or_equal_modifier(self, field: str, value: Union[int, str]) -> str: + value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True) + return f"{field} <= {value}" + + def greater_modifier(self, field: str, value: Union[int, str]) -> str: + value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True) + return f"{field} > {value}" + + def greater_or_equal_modifier(self, field: str, value: Union[int, str]) -> str: + value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True) + return f"{field} >= {value}" + + def not_equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: + if isinstance(value, list): + values = self.list_token.join( + self._pre_process_value(field, v, value_type=ValueType.value, wrap_str=True, wrap_int=True) + for v in value + ) + return f"{field} != ({values})" + value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True, wrap_int=True) + return f"{field} != {value}" + + def contains_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: + if isinstance(value, list): + values = self.list_token.join( + self._pre_process_value(field, v, value_type=ValueType.value, wrap_str=True, wrap_int=True) + for v in value + ) + return f"{field} contains ({values})" + value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True, wrap_int=True) + return f"{field} contains {value}" + + def endswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: + return self.contains_modifier(field, value) + + def startswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: + return self.contains_modifier(field, value) + + def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: + if isinstance(value, list): + values = self.list_token.join( + self.str_value_manager.escape_manager.escape( + self._pre_process_value(field, v, value_type=ValueType.regex_value, wrap_str=True, wrap_int=True) + ) + for v in value + ) + return f"{field} matches ({values})" + value = self._pre_process_value(field, value, value_type=ValueType.regex_value, wrap_str=True, wrap_int=True) + value = self.str_value_manager.escape_manager.escape(value) + return f"{field} matches {value}" + + def is_none(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002 + return f'not ({field} matches "\\.*")' + + def is_not_none(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002 + return f'{field} matches "\\.*"' + + +@render_manager.register +class SentinelOnePowerQueryRender(PlatformQueryRender): + details: PlatformDetails = sentinel_one_power_query_details + mappings: SentinelOnePowerQueryMappings = sentinel_one_power_query_query_mappings + or_token = "or" + and_token = "and" + not_token = "not" + comment_symbol = "//" + field_value_render = SentinelOnePowerQueryFieldValue(or_token=or_token)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: