Skip to content

Gis 7956 #154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion uncoder-core/app/translator/core/context_vars.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from contextvars import ContextVar

return_only_first_query_ctx_var: ContextVar[bool] = ContextVar("return_only_first_query_ctx_var", default=False)
"""Set to True to return ony first query if rendered multiple options"""
"""Set to True to return only first query if rendered multiple options"""
5 changes: 5 additions & 0 deletions uncoder-core/app/translator/core/models/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ def value(self) -> Union[int, str, StrValue, list[Union[int, str, StrValue]]]:
return self.values[0]
return self.values

@value.setter
def value(self, new_value: Union[int, str, StrValue, list[Union[int, str, StrValue]]]) -> None:
self.values = []
self.__add_value(new_value)

def __add_value(self, value: Optional[Union[int, str, StrValue, list, tuple]]) -> None:
if value and isinstance(value, (list, tuple)):
for v in value:
Expand Down
48 changes: 31 additions & 17 deletions uncoder-core/app/translator/core/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
limitations under the License.
-----------------------------------------------------------------
"""

from abc import ABC, abstractmethod
from collections.abc import Callable
from typing import Optional, Union
from typing import ClassVar, Optional, Union

from app.translator.const import DEFAULT_VALUE_TYPE
from app.translator.core.context_vars import return_only_first_query_ctx_var
Expand Down Expand Up @@ -165,7 +166,14 @@ class QueryRender(ABC):
is_single_line_comment: bool = False
unsupported_functions_text = "Unsupported functions were excluded from the result query:"

platform_functions: PlatformFunctions = PlatformFunctions()
platform_functions: PlatformFunctions = None

def __init__(self):
self.init_platform_functions()

def init_platform_functions(self) -> None:
self.platform_functions = PlatformFunctions()
self.platform_functions.platform_query_render = self

def render_not_supported_functions(self, not_supported_functions: list) -> str:
line_template = f"{self.comment_symbol} " if self.comment_symbol and self.is_single_line_comment else ""
Expand All @@ -192,19 +200,19 @@ class PlatformQueryRender(QueryRender):

field_value_map = BaseQueryFieldValue(or_token=or_token)

query_pattern = "{table} {query} {functions}"
raw_log_field_pattern_map: dict = None
raw_log_field_pattern_map: ClassVar[dict[str, str]] = None

def __init__(self):
super().__init__()
self.operator_map = {
LogicalOperatorType.AND: f" {self.and_token} ",
LogicalOperatorType.OR: f" {self.or_token} ",
LogicalOperatorType.NOT: f" {self.not_token} ",
}

def generate_prefix(self, log_source_signature: LogSourceSignature, functions_prefix: str = "") -> str: # noqa: ARG002
if str(log_source_signature):
return f"{log_source_signature!s} {self.and_token}"
def generate_prefix(self, log_source_signature: Optional[LogSourceSignature], functions_prefix: str = "") -> str: # noqa: ARG002
if log_source_signature and str(log_source_signature):
return f"{log_source_signature} {self.and_token}"
return ""

def generate_functions(self, functions: list[Function], source_mapping: SourceMapping) -> RenderedFunctions:
Expand Down Expand Up @@ -272,6 +280,10 @@ def wrap_query_with_meta_info(self, meta_info: MetaInfoContainer, query: str) ->
query = f"{query}\n\n{query_meta_info}"
return query

@staticmethod
def _finalize_search_query(query: str) -> str:
return query

def finalize_query(
self,
prefix: str,
Expand All @@ -283,8 +295,8 @@ def finalize_query(
*args, # noqa: ARG002
**kwargs, # noqa: ARG002
) -> str:
query = self.query_pattern.format(prefix=prefix, query=query, functions=functions).strip()

parts = filter(lambda s: bool(s), map(str.strip, [prefix, self._finalize_search_query(query), functions]))
query = " ".join(parts)
query = self.wrap_query_with_meta_info(meta_info=meta_info, query=query)
if not_supported_functions:
rendered_not_supported = self.render_not_supported_functions(not_supported_functions)
Expand Down Expand Up @@ -327,15 +339,15 @@ def _generate_from_raw_query_container(self, query_container: RawQueryContainer)

def process_raw_log_field(self, field: str, field_type: str) -> Optional[str]:
if raw_log_field_pattern := self.raw_log_field_pattern_map.get(field_type):
return raw_log_field_pattern.pattern.format(field=field)
return raw_log_field_pattern.format(field=field)

def process_raw_log_field_prefix(self, field: str, source_mapping: SourceMapping) -> Optional[list]:
if isinstance(field, list):
list_of_prefix = []
prefix_list = []
for f in field:
if prepared_prefix := self.process_raw_log_field_prefix(field=f, source_mapping=source_mapping):
list_of_prefix.extend(prepared_prefix)
return list_of_prefix
if _prefix_list := self.process_raw_log_field_prefix(field=f, source_mapping=source_mapping):
prefix_list.extend(_prefix_list)
return prefix_list
if raw_log_field_type := source_mapping.raw_log_fields.get(field):
return [self.process_raw_log_field(field=field, field_type=raw_log_field_type)]

Expand All @@ -352,9 +364,11 @@ def generate_raw_log_fields(self, fields: list[Field], source_mapping: SourceMap
)
if not mapped_field and self.is_strict_mapping:
raise StrictPlatformException(field_name=field.source_name, platform_name=self.details.name)
if field_prefix := self.process_raw_log_field_prefix(field=mapped_field, source_mapping=source_mapping):
defined_raw_log_fields.extend(field_prefix)
return "\n".join(set(defined_raw_log_fields))
if prefix_list := self.process_raw_log_field_prefix(field=mapped_field, source_mapping=source_mapping):
for prefix in prefix_list:
if prefix not in defined_raw_log_fields:
defined_raw_log_fields.append(prefix)
return "\n".join(defined_raw_log_fields)

def _generate_from_tokenized_query_container(self, query_container: TokenizedQueryContainer) -> str:
queries_map = {}
Expand Down
1 change: 1 addition & 0 deletions uncoder-core/app/translator/core/str_value_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from typing import ClassVar, Optional, TypeVar, Union

from app.translator.core.custom_types.values import ValueType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ field_mapping:
OldTargetUserName: xdm.target.user.username
UserPrincipalName: xdm.source.user.username
DestAddress: xdm.target.ipv4
SubjectAccountName: xdm.source.user.username
SubjectUserName: xdm.source.user.username
SubjectUserSid: xdm.source.user.identifier
SourceAddr: xdm.source.ipv4
Expand Down Expand Up @@ -117,7 +118,6 @@ field_mapping:
method: xdm.network.http.method
notice.user_agent: xdm.network.http.browser
hasIdentity: xdm.source.user.identity_type
SubjectAccountName: xdm.source.user.username
ComputerName: xdm.source.host.hostname
ExternalSeverity: xdm.alert.severity
SourceMAC: xdm.source.host.mac_addresses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ field_mapping:
ParentIntegrityLevel: causality_actor_process_integrity_level
ParentLogonId: causality_actor_process_logon_id
ParentProduct: causality_actor_process_signature_product
ParentCompany: causality_actor_process_signature_vendor
ParentCompany: causality_actor_process_signature_vendor
EventType: event_sub_type
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ default_log_source:
field_mapping:
EventID: action_evtlog_event_id
Provider_Name: provider_name

SubjectAccountName: actor_effective_username

raw_log_fields:
ParentImage: regex
AccessMask: regex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ field_mapping:
NewValue: NewValue
Source: Source
Status: Status
SubjectAccountName:
- Subject Account Name
- SubjectAccountName
SubjectDomainName: SubjectDomainName
SubjectUserName: Target Username
SubjectUserSid: SubjectUserSid
Expand Down Expand Up @@ -171,5 +174,4 @@ field_mapping:
UserID: UserID
ParentProcessName: Parent Process Name
Service: Service
hasIdentity: hasIdentity
SubjectAccountName: SubjectAccountName
hasIdentity: hasIdentity
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from app.translator.core.models.platform_details import PlatformDetails
from app.translator.managers import render_manager
from app.translator.platforms.athena.const import athena_details
Expand All @@ -35,6 +36,9 @@ class AthenaQueryRender(SqlQueryRender):
or_token = "OR"

field_value_map = AthenaFieldValue(or_token=or_token)
query_pattern = "{prefix} WHERE {query} {functions}"
comment_symbol = "--"
is_single_line_comment = True

@staticmethod
def _finalize_search_query(query: str) -> str:
return f"WHERE {query}" if query else ""
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from typing import Union

from app.translator.const import DEFAULT_VALUE_TYPE
Expand Down Expand Up @@ -127,7 +128,6 @@ class AQLQueryRender(PlatformQueryRender):
not_token = "NOT"

field_value_map = AQLFieldValue(or_token=or_token)
query_pattern = "{prefix} AND {query} {functions}"

def generate_prefix(self, log_source_signature: AQLLogSourceSignature, functions_prefix: str = "") -> str: # noqa: ARG002
table = str(log_source_signature)
Expand All @@ -136,3 +136,7 @@ def generate_prefix(self, log_source_signature: AQLLogSourceSignature, functions

def wrap_with_comment(self, value: str) -> str:
return f"/* {value} */"

@staticmethod
def _finalize_search_query(query: str) -> str:
return f"AND {query}" if query else ""
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from typing import Optional, Union

from app.translator.const import DEFAULT_VALUE_TYPE
Expand Down Expand Up @@ -106,8 +107,6 @@ class LuceneQueryRender(PlatformQueryRender):
and_token = "AND"
not_token = "NOT"

query_pattern = "{query} {functions}"

comment_symbol = "//"
is_single_line_comment = True

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from typing import Union

from app.translator.const import DEFAULT_VALUE_TYPE
Expand Down Expand Up @@ -78,7 +79,6 @@ class SplQueryRender(PlatformQueryRender):
and_token = "AND"
not_token = "NOT"

query_pattern = "{prefix} {query} {functions}"
comment_symbol = "```"

def wrap_with_comment(self, value: str) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from typing import Union

from app.translator.const import DEFAULT_VALUE_TYPE
Expand Down Expand Up @@ -76,10 +77,13 @@ class SqlQueryRender(PlatformQueryRender):
and_token = "AND"
not_token = "NOT"

query_pattern = "{prefix} WHERE {query} {functions}"
comment_symbol = "--"
is_single_line_comment = True

def generate_prefix(self, log_source_signature: LogSourceSignature, functions_prefix: str = "") -> str: # noqa: ARG002
table = str(log_source_signature) if str(log_source_signature) else "eventlog"
return f"SELECT * FROM {table}"

@staticmethod
def _finalize_search_query(query: str) -> str:
return f"WHERE {query}" if query else ""
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from typing import Union

from app.translator.const import DEFAULT_VALUE_TYPE
Expand Down Expand Up @@ -109,6 +110,5 @@ class ChronicleQueryRender(PlatformQueryRender):
not_token = "not"

field_value_map = ChronicleFieldValue(or_token=or_token)
query_pattern = "{query} {functions}"
comment_symbol = "//"
is_single_line_comment = True
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from app.translator.core.models.platform_details import PlatformDetails
from app.translator.managers import render_manager
from app.translator.platforms.base.spl.renders.spl import SplFieldValue, SplQueryRender
Expand All @@ -31,14 +32,13 @@ class CrowdStrikeFieldValue(SplFieldValue):
@render_manager.register
class CrowdStrikeQueryRender(SplQueryRender):
details: PlatformDetails = crowdstrike_query_details
query_pattern = "{prefix} {query} {functions}"
mappings: CrowdstrikeMappings = crowdstrike_mappings
platform_functions: CrowdStrikeFunctions = crowd_strike_functions
platform_functions: CrowdStrikeFunctions = None

or_token = "OR"
field_value_map = CrowdStrikeFieldValue(or_token=or_token)
comment_symbol = "`"

def __init__(self):
super().__init__()
self.platform_functions.manager.post_init_configure(self)
def init_platform_functions(self) -> None:
self.platform_functions = crowd_strike_functions
self.platform_functions.platform_query_render = self
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ class ElasticSearchRuleRender(ElasticSearchQueryRender):
not_token = "NOT"

field_value_map = ElasticSearchRuleFieldValue(or_token=or_token)
query_pattern = "{prefix} {query} {functions}"

def __create_mitre_threat(self, mitre_attack: dict) -> Union[list, list[dict]]:
if not mitre_attack.get("techniques"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
limitations under the License.
-----------------------------------------------------------------
"""

from typing import Optional

from app.translator.core.custom_types.meta_info import SeverityType
Expand Down Expand Up @@ -49,7 +50,6 @@ class ElastAlertRuleRender(ElasticSearchQueryRender):
not_token = "NOT"

field_value_map = ElasticAlertRuleFieldValue(or_token=or_token)
query_pattern = "{prefix} {query} {functions}"

def finalize_query(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from typing import Optional, Union

from app.translator.const import DEFAULT_VALUE_TYPE
from app.translator.core.context_vars import return_only_first_query_ctx_var
from app.translator.core.custom_types.meta_info import SeverityType
from app.translator.core.custom_types.tokens import GroupType, LogicalOperatorType, OperatorType
from app.translator.core.custom_types.values import ValueType
Expand Down Expand Up @@ -192,7 +193,6 @@ class FortiSiemRuleRender(PlatformQueryRender):
not_token = None

group_token = "(%s)"
query_pattern = "{prefix} {query}"

field_value_map = FortiSiemFieldValue(or_token=or_token)

Expand Down Expand Up @@ -273,6 +273,8 @@ def _generate_from_tokenized_query_container(self, query_container: TokenizedQue
source_mapping=source_mapping,
fields=mapped_fields_set,
)
if return_only_first_query_ctx_var.get() is True:
return finalized_query
queries_map[source_mapping.source_id] = finalized_query

return self.finalize(queries_map)
Expand Down Expand Up @@ -301,15 +303,15 @@ def finalize_query(
self,
prefix: str,
query: str,
functions: str, # noqa: ARG002
functions: str,
meta_info: Optional[MetaInfoContainer] = None,
source_mapping: Optional[SourceMapping] = None, # noqa: ARG002
not_supported_functions: Optional[list] = None,
fields: Optional[set[str]] = None,
*args, # noqa: ARG002
**kwargs, # noqa: ARG002
) -> str:
query = self.query_pattern.format(prefix=prefix, query=query).strip()
query = super().finalize_query(prefix=prefix, query=query, functions=functions)
rule = FORTI_SIEM_RULE.replace("<header_placeholder>", self.generate_rule_header(meta_info))
title = meta_info.title or _AUTOGENERATED_TEMPLATE
rule = rule.replace("<name_placeholder>", self.generate_rule_name(title))
Expand Down
Loading
Loading