From 3c90bed0d0b2eb31646db9d442202e5a883c98d5 Mon Sep 17 00:00:00 2001 From: Oleksandr Volha Date: Thu, 7 Mar 2024 16:31:49 +0200 Subject: [PATCH 1/4] one vendor translations flow --- .../app/translator/core/exceptions/core.py | 6 +- .../translator/core/exceptions/functions.py | 3 +- .../app/translator/core/exceptions/iocs.py | 6 +- .../app/translator/core/exceptions/parser.py | 3 +- .../app/translator/core/exceptions/render.py | 6 +- translator/app/translator/core/functions.py | 22 +---- .../{parser_output.py => query_container.py} | 16 ++- translator/app/translator/core/parser.py | 24 ++--- translator/app/translator/core/render.py | 43 +++++--- translator/app/translator/core/render_cti.py | 1 - .../app/translator/core/str_value_manager.py | 63 ++++-------- .../app/translator/platforms/__init__.py | 50 +++++----- .../platforms/athena/parsers/athena.py | 31 +++--- .../platforms/athena/renders/athena.py | 5 +- .../platforms/base/lucene/parsers/lucene.py | 26 ++--- .../platforms/base/lucene/renders/lucene.py | 5 +- .../platforms/base/lucene/tokenizer.py | 1 + .../platforms/base/spl/parsers/spl.py | 24 ++--- .../platforms/base/spl/renders/spl.py | 5 +- .../translator/platforms/chronicle/mapping.py | 3 +- .../platforms/chronicle/parsers/chronicle.py | 21 ++-- .../chronicle/parsers/chronicle_rule.py | 64 ++++++------ .../platforms/chronicle/renders/chronicle.py | 5 +- .../chronicle/renders/chronicle_rule.py | 2 +- .../crowdstrike/parsers/crowdstrike.py | 5 +- .../crowdstrike/renders/crowdstrike.py | 1 + .../platforms/elasticsearch/const.py | 24 ++++- .../elasticsearch/parsers/detection_rule.py | 33 ++----- .../elasticsearch/parsers/elasticsearch.py | 4 +- .../elasticsearch/renders/detection_rule.py | 2 +- .../elasticsearch/renders/elast_alert.py | 3 +- .../platforms/elasticsearch/renders/kibana.py | 3 +- .../elasticsearch/renders/xpack_watcher.py | 3 +- .../platforms/elasticsearch/tokenizer.py | 1 + .../forti_siem/renders/forti_siem_rule.py | 21 ++-- .../platforms/graylog/parsers/graylog.py | 4 +- .../platforms/graylog/renders/graylog.py | 2 +- .../renders/logrhythm_axon_query.py | 27 +++--- .../renders/logrhythm_axon_rule.py | 3 +- .../platforms/logscale/functions/const.py | 1 - .../platforms/logscale/parsers/logscale.py | 27 ++---- .../logscale/parsers/logscale_alert.py | 35 ++----- .../platforms/logscale/renders/logscale.py | 7 +- .../logscale/renders/logscale_alert.py | 3 +- .../microsoft/parsers/microsoft_defender.py | 4 +- .../microsoft/parsers/microsoft_sentinel.py | 23 ++--- .../parsers/microsoft_sentinel_rule.py | 30 ++---- .../renders/microsoft_defender_cti.py | 1 + .../microsoft/renders/microsoft_sentinel.py | 5 +- .../renders/microsoft_sentinel_rule.py | 3 +- .../opensearch/parsers/opensearch.py | 4 +- .../opensearch/renders/opensearch.py | 1 + .../opensearch/renders/opensearch_rule.py | 3 +- .../platforms/opensearch/tokenizer.py | 1 + .../platforms/qradar/escape_manager.py | 3 +- .../platforms/qradar/parsers/qradar.py | 20 ++-- .../platforms/qradar/renders/qradar.py | 5 +- .../platforms/roota/parsers/roota.py | 55 ++++++----- .../app/translator/platforms/sigma/const.py | 4 +- .../platforms/sigma/parsers/sigma.py | 9 +- .../platforms/sigma/renders/sigma.py | 22 +++-- .../platforms/splunk/parsers/splunk.py | 4 +- .../platforms/splunk/parsers/splunk_alert.py | 25 ++--- .../platforms/splunk/renders/splunk.py | 1 + .../platforms/splunk/renders/splunk_alert.py | 3 +- translator/app/translator/tools/decorators.py | 5 +- translator/app/translator/translator.py | 97 +++++++++++++------ 67 files changed, 465 insertions(+), 507 deletions(-) rename translator/app/translator/core/models/{parser_output.py => query_container.py} (85%) diff --git a/translator/app/translator/core/exceptions/core.py b/translator/app/translator/core/exceptions/core.py index 8f7d47fc..93d77a54 100644 --- a/translator/app/translator/core/exceptions/core.py +++ b/translator/app/translator/core/exceptions/core.py @@ -1,9 +1,7 @@ -class NotImplementedException(BaseException): - ... +class NotImplementedException(BaseException): ... -class BasePlatformException(BaseException): - ... +class BasePlatformException(BaseException): ... class StrictPlatformException(BasePlatformException): diff --git a/translator/app/translator/core/exceptions/functions.py b/translator/app/translator/core/exceptions/functions.py index 17a956d3..53c1a60f 100644 --- a/translator/app/translator/core/exceptions/functions.py +++ b/translator/app/translator/core/exceptions/functions.py @@ -1,5 +1,4 @@ -class BaseFunctionException(Exception): - ... +class BaseFunctionException(Exception): ... class InternalFunctionException(Exception): diff --git a/translator/app/translator/core/exceptions/iocs.py b/translator/app/translator/core/exceptions/iocs.py index 6ed9b988..7c3966df 100644 --- a/translator/app/translator/core/exceptions/iocs.py +++ b/translator/app/translator/core/exceptions/iocs.py @@ -1,9 +1,7 @@ -class BaseIOCsException(BaseException): - ... +class BaseIOCsException(BaseException): ... -class IocsLimitExceededException(BaseIOCsException): - ... +class IocsLimitExceededException(BaseIOCsException): ... class EmptyIOCSException(BaseIOCsException): diff --git a/translator/app/translator/core/exceptions/parser.py b/translator/app/translator/core/exceptions/parser.py index 6c9a8e08..0468bec7 100644 --- a/translator/app/translator/core/exceptions/parser.py +++ b/translator/app/translator/core/exceptions/parser.py @@ -1,5 +1,4 @@ -class BaseParserException(BaseException): - ... +class BaseParserException(BaseException): ... class TokenizerGeneralException(BaseParserException): diff --git a/translator/app/translator/core/exceptions/render.py b/translator/app/translator/core/exceptions/render.py index 4dd14b35..8467f5c9 100644 --- a/translator/app/translator/core/exceptions/render.py +++ b/translator/app/translator/core/exceptions/render.py @@ -1,5 +1,4 @@ -class BaseRenderException(BaseException): - ... +class BaseRenderException(BaseException): ... class UnexpectedLogsourceException(BaseRenderException): @@ -8,8 +7,7 @@ def __init__(self, platform_name: str, log_source: str): super().__init__(message) -class FunctionRenderException(BaseRenderException): - ... +class FunctionRenderException(BaseRenderException): ... class UnsupportedRenderMethod(BaseRenderException): diff --git a/translator/app/translator/core/functions.py b/translator/app/translator/core/functions.py index 7c0080ca..db595235 100644 --- a/translator/app/translator/core/functions.py +++ b/translator/app/translator/core/functions.py @@ -1,21 +1,3 @@ -""" -Uncoder IO Commercial Edition License ------------------------------------------------------------------ -Copyright (c) 2023 SOC Prime, Inc. - -This file is part of the Uncoder IO Commercial Edition ("CE") and is -licensed under the Uncoder IO Non-Commercial License (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - https://github.com/UncoderIO/UncoderIO/blob/main/LICENSE - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ------------------------------------------------------------------ -""" - from __future__ import annotations from abc import ABC, abstractmethod @@ -30,7 +12,7 @@ from settings import INIT_FUNCTIONS if TYPE_CHECKING: - from app.translator.core.render import BaseQueryRender + from app.translator.core.render import PlatformQueryRender class FunctionParser(ABC): @@ -72,7 +54,7 @@ def __init__(self): self._names_map: dict[str, str] = {} @abstractmethod - def init_search_func_render(self, platform_render: BaseQueryRender) -> None: + def init_search_func_render(self, platform_render: PlatformQueryRender) -> None: raise NotImplementedError @cached_property diff --git a/translator/app/translator/core/models/parser_output.py b/translator/app/translator/core/models/query_container.py similarity index 85% rename from translator/app/translator/core/models/parser_output.py rename to translator/app/translator/core/models/query_container.py index a17dbc43..b708127c 100644 --- a/translator/app/translator/core/models/parser_output.py +++ b/translator/app/translator/core/models/query_container.py @@ -7,6 +7,7 @@ from app.translator.core.mapping import DEFAULT_MAPPING_NAME from app.translator.core.models.field import Field from app.translator.core.models.functions.base import ParsedFunctions +from app.translator.core.tokenizer import TOKEN_TYPE class MetaInfoContainer: @@ -27,15 +28,15 @@ def __init__( status: Optional[str] = None, false_positives: Optional[list[str]] = None, source_mapping_ids: Optional[list[str]] = None, - parsed_logsources: Optional[dict] = None + parsed_logsources: Optional[dict] = None, ) -> None: self.id = id_ or str(uuid.uuid4()) self.title = title or "" self.description = description or "" self.author = author or "" self.date = date or datetime.now().date().strftime("%Y-%m-%d") - self.license = license_ or "DRL 1.1" self.fields = fields or [] + self.license = license_ or "DRL 1.1" self.severity = severity or SeverityType.low self.references = references or [] self.tags = tags or [] @@ -47,7 +48,14 @@ def __init__( @dataclass -class SiemContainer: - query: list +class RawQueryContainer: + query: str + language: str + meta_info: MetaInfoContainer = field(default_factory=MetaInfoContainer) + + +@dataclass +class TokenizedQueryContainer: + tokens: list[TOKEN_TYPE] meta_info: MetaInfoContainer functions: ParsedFunctions = field(default_factory=ParsedFunctions) diff --git a/translator/app/translator/core/parser.py b/translator/app/translator/core/parser.py index 64097a4a..07450236 100644 --- a/translator/app/translator/core/parser.py +++ b/translator/app/translator/core/parser.py @@ -17,33 +17,35 @@ """ from abc import ABC, abstractmethod +from typing import Union from app.translator.core.exceptions.parser import TokenizerGeneralException from app.translator.core.functions import PlatformFunctions from app.translator.core.mapping import BasePlatformMappings, SourceMapping from app.translator.core.models.field import FieldValue from app.translator.core.models.functions.base import ParsedFunctions -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer from app.translator.core.tokenizer import TOKEN_TYPE, QueryTokenizer -class Parser(ABC): - mappings: BasePlatformMappings = None - tokenizer: QueryTokenizer = None - details: PlatformDetails = None - platform_functions: PlatformFunctions = None +class QueryParser(ABC): + def parse_raw_query(self, text: str, language: str) -> RawQueryContainer: + return RawQueryContainer(query=text, language=language) @abstractmethod - def _get_meta_info(self, *args, **kwargs) -> MetaInfoContainer: + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: raise NotImplementedError("Abstract method") - @abstractmethod - def parse(self, text: str) -> SiemContainer: - raise NotImplementedError("Abstract method") + +class PlatformQueryParser(QueryParser, ABC): + mappings: BasePlatformMappings = None + tokenizer: QueryTokenizer = None + details: PlatformDetails = None + platform_functions: PlatformFunctions = None def get_tokens_and_source_mappings( - self, query: str, log_sources: dict[str, list[str]] + self, query: str, log_sources: dict[str, Union[str, list[str]]] ) -> tuple[list[TOKEN_TYPE], list[SourceMapping]]: if not query: raise TokenizerGeneralException("Can't translate empty query. Please provide more details") diff --git a/translator/app/translator/core/render.py b/translator/app/translator/core/render.py index c26416bf..039a8d53 100644 --- a/translator/app/translator/core/render.py +++ b/translator/app/translator/core/render.py @@ -16,7 +16,8 @@ limitations under the License. ----------------------------------------------------------------- """ -from abc import ABC + +from abc import ABC, abstractmethod from collections.abc import Callable from typing import Optional, Union @@ -29,11 +30,12 @@ from app.translator.core.functions import PlatformFunctions from app.translator.core.mapping import DEFAULT_MAPPING_NAME, BasePlatformMappings, LogSourceSignature, SourceMapping from app.translator.core.models.field import Field, FieldValue, Keyword -from app.translator.core.models.functions.base import Function, ParsedFunctions +from app.translator.core.models.functions.base import Function from app.translator.core.models.identifier import Identifier -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer, RawQueryContainer, TokenizedQueryContainer from app.translator.core.str_value_manager import StrValueManager +from app.translator.core.tokenizer import TOKEN_TYPE class BaseQueryFieldValue(ABC): @@ -99,7 +101,13 @@ def apply_field_value(self, field: str, operator: Identifier, value: DEFAULT_VAL raise UnsupportedOperatorException(operator.token_type) -class BaseQueryRender: +class QueryRender(ABC): + @abstractmethod + def generate(self, query_container: Union[RawQueryContainer, TokenizedQueryContainer]) -> str: + raise NotImplementedError("Abstract method") + + +class PlatformQueryRender(QueryRender): mappings: BasePlatformMappings = None details: PlatformDetails = None is_strict_mapping = False @@ -168,9 +176,9 @@ def apply_token(self, token: Union[FieldValue, Keyword, Identifier], source_mapp return token.token_type - def generate_query(self, query: list[Union[FieldValue, Keyword, Identifier]], source_mapping: SourceMapping) -> str: + def generate_query(self, tokens: list[TOKEN_TYPE], source_mapping: SourceMapping) -> str: result_values = [] - for token in query: + for token in tokens: result_values.append(self.apply_token(token=token, source_mapping=source_mapping)) return "".join(result_values) @@ -243,22 +251,33 @@ def _get_source_mappings(self, source_mapping_ids: list[str]) -> list[SourceMapp return source_mappings - def generate(self, query: list, meta_info: MetaInfoContainer, functions: ParsedFunctions) -> str: + 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 + ) + + def _generate_from_tokenized_query_container(self, query_container: TokenizedQueryContainer) -> str: queries_map = {} - source_mappings = self._get_source_mappings(meta_info.source_mapping_ids) + source_mappings = self._get_source_mappings(query_container.meta_info.source_mapping_ids) for source_mapping in source_mappings: prefix = self.generate_prefix(source_mapping.log_source_signature) - result = self.generate_query(query=query, source_mapping=source_mapping) + result = self.generate_query(tokens=query_container.tokens, source_mapping=source_mapping) finalized_query = self.finalize_query( prefix=prefix, query=result, - functions=self.generate_functions(functions.functions, source_mapping), - not_supported_functions=functions.not_supported, - meta_info=meta_info, + functions=self.generate_functions(query_container.functions.functions, source_mapping), + not_supported_functions=query_container.functions.not_supported, + meta_info=query_container.meta_info, source_mapping=source_mapping, ) queries_map[source_mapping.source_id] = finalized_query return self.finalize(queries_map) + + def generate(self, query_container: Union[RawQueryContainer, TokenizedQueryContainer]) -> str: + if isinstance(query_container, RawQueryContainer): + return self._generate_from_raw_query_container(query_container) + + return self._generate_from_tokenized_query_container(query_container) diff --git a/translator/app/translator/core/render_cti.py b/translator/app/translator/core/render_cti.py index f07d5c15..30509a71 100644 --- a/translator/app/translator/core/render_cti.py +++ b/translator/app/translator/core/render_cti.py @@ -17,7 +17,6 @@ ----------------------------------------------------------------- """ - from app.translator.core.models.iocs import IocsChunkValue diff --git a/translator/app/translator/core/str_value_manager.py b/translator/app/translator/core/str_value_manager.py index 729a027a..355ebbbf 100644 --- a/translator/app/translator/core/str_value_manager.py +++ b/translator/app/translator/core/str_value_manager.py @@ -22,91 +22,70 @@ from app.translator.core.escape_manager import EscapeManager -class BaseSpecSymbol: - ... +class BaseSpecSymbol: ... SpecSymbolType = TypeVar("SpecSymbolType", bound=BaseSpecSymbol) -class SingleSymbolWildCard(BaseSpecSymbol): - ... +class SingleSymbolWildCard(BaseSpecSymbol): ... -class UnboundLenWildCard(BaseSpecSymbol): - ... +class UnboundLenWildCard(BaseSpecSymbol): ... -class ReEndOfStrSymbol(BaseSpecSymbol): - ... +class ReEndOfStrSymbol(BaseSpecSymbol): ... -class ReWordSymbol(BaseSpecSymbol): - ... +class ReWordSymbol(BaseSpecSymbol): ... -class ReDigitalSymbol(BaseSpecSymbol): - ... +class ReDigitalSymbol(BaseSpecSymbol): ... -class ReAnySymbol(BaseSpecSymbol): - ... +class ReAnySymbol(BaseSpecSymbol): ... -class ReWhiteSpaceSymbol(BaseSpecSymbol): - ... +class ReWhiteSpaceSymbol(BaseSpecSymbol): ... -class ReOneOrMoreQuantifier(BaseSpecSymbol): - ... +class ReOneOrMoreQuantifier(BaseSpecSymbol): ... -class ReZeroOrMoreQuantifier(BaseSpecSymbol): - ... +class ReZeroOrMoreQuantifier(BaseSpecSymbol): ... -class ReZeroOrOneQuantifier(BaseSpecSymbol): - ... +class ReZeroOrOneQuantifier(BaseSpecSymbol): ... -class ReLeftParenthesis(BaseSpecSymbol): - ... +class ReLeftParenthesis(BaseSpecSymbol): ... -class ReRightParenthesis(BaseSpecSymbol): - ... +class ReRightParenthesis(BaseSpecSymbol): ... -class ReLeftSquareBracket(BaseSpecSymbol): - ... +class ReLeftSquareBracket(BaseSpecSymbol): ... -class ReRightSquareBracket(BaseSpecSymbol): - ... +class ReRightSquareBracket(BaseSpecSymbol): ... -class ReLeftCurlyBracket(BaseSpecSymbol): - ... +class ReLeftCurlyBracket(BaseSpecSymbol): ... -class ReRightCurlyBracket(BaseSpecSymbol): - ... +class ReRightCurlyBracket(BaseSpecSymbol): ... -class ReOrOperator(BaseSpecSymbol): - ... +class ReOrOperator(BaseSpecSymbol): ... -class ReCaretSymbol(BaseSpecSymbol): - ... +class ReCaretSymbol(BaseSpecSymbol): ... -class ReCommaSymbol(BaseSpecSymbol): - ... +class ReCommaSymbol(BaseSpecSymbol): ... -class ReHyphenSymbol(BaseSpecSymbol): - ... +class ReHyphenSymbol(BaseSpecSymbol): ... class StrValue(str): diff --git a/translator/app/translator/platforms/__init__.py b/translator/app/translator/platforms/__init__.py index af92d5be..4a30fd41 100644 --- a/translator/app/translator/platforms/__init__.py +++ b/translator/app/translator/platforms/__init__.py @@ -1,17 +1,17 @@ -from app.translator.platforms.athena.parsers.athena import AthenaParser +from app.translator.platforms.athena.parsers.athena import AthenaQueryParser from app.translator.platforms.athena.renders.athena import AthenaQueryRender from app.translator.platforms.athena.renders.athena_cti import AthenaCTI from app.translator.platforms.carbonblack.renders.carbonblack_cti import CarbonBlackCTI -from app.translator.platforms.chronicle.parsers.chronicle import ChronicleParser +from app.translator.platforms.chronicle.parsers.chronicle import ChronicleQueryParser from app.translator.platforms.chronicle.parsers.chronicle_rule import ChronicleRuleParser from app.translator.platforms.chronicle.renders.chronicle import ChronicleQueryRender from app.translator.platforms.chronicle.renders.chronicle_cti import ChronicleQueryCTI from app.translator.platforms.chronicle.renders.chronicle_rule import ChronicleSecurityRuleRender -from app.translator.platforms.crowdstrike.parsers.crowdstrike import CrowdStrikeParser +from app.translator.platforms.crowdstrike.parsers.crowdstrike import CrowdStrikeQueryParser from app.translator.platforms.crowdstrike.renders.crowdstrike import CrowdStrikeQueryRender from app.translator.platforms.crowdstrike.renders.crowdstrike_cti import CrowdStrikeCTI from app.translator.platforms.elasticsearch.parsers.detection_rule import ElasticSearchRuleParser -from app.translator.platforms.elasticsearch.parsers.elasticsearch import ElasticSearchParser +from app.translator.platforms.elasticsearch.parsers.elasticsearch import ElasticSearchQueryParser from app.translator.platforms.elasticsearch.renders.detection_rule import ElasticSearchRuleRender from app.translator.platforms.elasticsearch.renders.elast_alert import ElastAlertRuleRender from app.translator.platforms.elasticsearch.renders.elasticsearch import ElasticSearchQueryRender @@ -20,30 +20,30 @@ from app.translator.platforms.elasticsearch.renders.xpack_watcher import XPackWatcherRuleRender from app.translator.platforms.fireeye_helix.renders.fireeye_helix_cti import FireeyeHelixCTI from app.translator.platforms.forti_siem.renders.forti_siem_rule import FortiSiemRuleRender -from app.translator.platforms.graylog.parsers.graylog import GraylogParser -from app.translator.platforms.graylog.renders.graylog import GraylogRender +from app.translator.platforms.graylog.parsers.graylog import GraylogQueryParser +from app.translator.platforms.graylog.renders.graylog import GraylogQueryRender from app.translator.platforms.graylog.renders.graylog_cti import GraylogCTI from app.translator.platforms.logpoint.renders.logpoint_cti import LogpointCTI from app.translator.platforms.logrhythm_axon.renders.logrhythm_axon_query import LogRhythmAxonQueryRender from app.translator.platforms.logrhythm_axon.renders.logrhythm_axon_rule import LogRhythmAxonRuleRender -from app.translator.platforms.logscale.parsers.logscale import LogScaleParser +from app.translator.platforms.logscale.parsers.logscale import LogScaleQueryParser from app.translator.platforms.logscale.parsers.logscale_alert import LogScaleAlertParser -from app.translator.platforms.logscale.renders.logscale_cti import LogScaleCTI from app.translator.platforms.logscale.renders.logscale import LogScaleQueryRender from app.translator.platforms.logscale.renders.logscale_alert import LogScaleAlertRender +from app.translator.platforms.logscale.renders.logscale_cti import LogScaleCTI from app.translator.platforms.microsoft.parsers.microsoft_defender import MicrosoftDefenderQueryParser -from app.translator.platforms.microsoft.parsers.microsoft_sentinel import MicrosoftParser -from app.translator.platforms.microsoft.parsers.microsoft_sentinel_rule import MicrosoftRuleParser +from app.translator.platforms.microsoft.parsers.microsoft_sentinel import MicrosoftSentinelQueryParser +from app.translator.platforms.microsoft.parsers.microsoft_sentinel_rule import MicrosoftSentinelRuleParser from app.translator.platforms.microsoft.renders.microsoft_defender import MicrosoftDefenderQueryRender from app.translator.platforms.microsoft.renders.microsoft_defender_cti import MicrosoftDefenderCTI from app.translator.platforms.microsoft.renders.microsoft_sentinel import MicrosoftSentinelQueryRender from app.translator.platforms.microsoft.renders.microsoft_sentinel_cti import MicrosoftSentinelCTI from app.translator.platforms.microsoft.renders.microsoft_sentinel_rule import MicrosoftSentinelRuleRender -from app.translator.platforms.opensearch.parsers.opensearch import OpenSearchParser +from app.translator.platforms.opensearch.parsers.opensearch import OpenSearchQueryParser from app.translator.platforms.opensearch.renders.opensearch import OpenSearchQueryRender from app.translator.platforms.opensearch.renders.opensearch_cti import OpenSearchCTI from app.translator.platforms.opensearch.renders.opensearch_rule import OpenSearchRuleRender -from app.translator.platforms.qradar.parsers.qradar import QradarParser +from app.translator.platforms.qradar.parsers.qradar import QradarQueryParser from app.translator.platforms.qradar.renders.qradar import QradarQueryRender from app.translator.platforms.qradar.renders.qradar_cti import QRadarCTI from app.translator.platforms.qualys.renders.qualys_cti import QualysCTI @@ -53,7 +53,7 @@ from app.translator.platforms.sigma.parsers.sigma import SigmaParser from app.translator.platforms.sigma.renders.sigma import SigmaRender from app.translator.platforms.snowflake.renders.snowflake_cti import SnowflakeCTI -from app.translator.platforms.splunk.parsers.splunk import SplunkParser +from app.translator.platforms.splunk.parsers.splunk import SplunkQueryParser from app.translator.platforms.splunk.parsers.splunk_alert import SplunkAlertParser from app.translator.platforms.splunk.renders.splunk import SplunkQueryRender from app.translator.platforms.splunk.renders.splunk_alert import SplunkAlertRender @@ -83,28 +83,28 @@ XPackWatcherRuleRender(), OpenSearchQueryRender(), OpenSearchRuleRender(), - GraylogRender(), + GraylogQueryRender(), FortiSiemRuleRender(), ) __ALL_PARSERS = ( - AthenaParser(), - ChronicleParser(), + AthenaQueryParser(), + ChronicleQueryParser(), ChronicleRuleParser(), - SplunkParser(), + SplunkQueryParser(), SplunkAlertParser(), SigmaParser(), - QradarParser(), - MicrosoftParser(), - MicrosoftRuleParser(), + QradarQueryParser(), + MicrosoftSentinelQueryParser(), + MicrosoftSentinelRuleParser(), MicrosoftDefenderQueryParser(), - CrowdStrikeParser(), - LogScaleParser(), + CrowdStrikeQueryParser(), + LogScaleQueryParser(), LogScaleAlertParser(), - ElasticSearchParser(), + ElasticSearchQueryParser(), ElasticSearchRuleParser(), - OpenSearchParser(), - GraylogParser(), + OpenSearchQueryParser(), + GraylogQueryParser(), ) diff --git a/translator/app/translator/platforms/athena/parsers/athena.py b/translator/app/translator/platforms/athena/parsers/athena.py index 63fa31ed..fe54e1f0 100644 --- a/translator/app/translator/platforms/athena/parsers/athena.py +++ b/translator/app/translator/platforms/athena/parsers/athena.py @@ -19,39 +19,34 @@ import re from typing import Optional -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser from app.translator.platforms.athena.const import athena_details from app.translator.platforms.athena.mapping import AthenaMappings, athena_mappings from app.translator.platforms.athena.tokenizer import AthenaTokenizer -class AthenaParser(Parser): +class AthenaQueryParser(PlatformQueryParser): details: PlatformDetails = athena_details mappings: AthenaMappings = athena_mappings tokenizer = AthenaTokenizer() query_delimiter_pattern = r"\sFROM\s\S*\sWHERE\s" table_pattern = r"\sFROM\s(?P[a-zA-Z\.\-\*]+)\sWHERE\s" - @staticmethod - def _get_meta_info(source_mapping_ids: list[str]) -> MetaInfoContainer: - return MetaInfoContainer(source_mapping_ids=source_mapping_ids) - - def _parse_query(self, text: str) -> tuple[str, dict[str, Optional[str]]]: + def _parse_query(self, query: str) -> tuple[str, dict[str, Optional[str]]]: log_source = {"table": None} - if re.search(self.query_delimiter_pattern, text, flags=re.IGNORECASE): - table_search = re.search(self.table_pattern, text) + if re.search(self.query_delimiter_pattern, query, flags=re.IGNORECASE): + table_search = re.search(self.table_pattern, query) table = table_search.group("table") log_source["table"] = table - return re.split(self.query_delimiter_pattern, text, flags=re.IGNORECASE)[1], log_source + return re.split(self.query_delimiter_pattern, query, flags=re.IGNORECASE)[1], log_source - return text, log_source + return query, log_source - def parse(self, text: str) -> SiemContainer: - query, log_sources = self._parse_query(text) + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: + query, log_sources = self._parse_query(raw_query_container.query) tokens, source_mappings = self.get_tokens_and_source_mappings(query, log_sources) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info([source_mapping.source_id for source_mapping in source_mappings]), - ) + meta_info = raw_query_container.meta_info + meta_info.source_mapping_ids = [source_mapping.source_id for source_mapping in source_mappings] + return TokenizedQueryContainer(tokens=tokens, meta_info=meta_info) diff --git a/translator/app/translator/platforms/athena/renders/athena.py b/translator/app/translator/platforms/athena/renders/athena.py index d32e5f8a..0efe4309 100644 --- a/translator/app/translator/platforms/athena/renders/athena.py +++ b/translator/app/translator/platforms/athena/renders/athena.py @@ -16,13 +16,14 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Union from app.translator.const import DEFAULT_VALUE_TYPE from app.translator.core.exceptions.render import UnsupportedRenderMethod from app.translator.core.mapping import LogSourceSignature from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.platforms.athena.const import athena_details from app.translator.platforms.athena.mapping import AthenaMappings, athena_mappings @@ -76,7 +77,7 @@ def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG00 raise UnsupportedRenderMethod(platform_name=self.details.name, method="Keywords") -class AthenaQueryRender(BaseQueryRender): +class AthenaQueryRender(PlatformQueryRender): details: PlatformDetails = athena_details mappings: AthenaMappings = athena_mappings diff --git a/translator/app/translator/platforms/base/lucene/parsers/lucene.py b/translator/app/translator/platforms/base/lucene/parsers/lucene.py index 886f11e1..ca4caf09 100644 --- a/translator/app/translator/platforms/base/lucene/parsers/lucene.py +++ b/translator/app/translator/platforms/base/lucene/parsers/lucene.py @@ -18,18 +18,18 @@ import re -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser from app.translator.platforms.base.lucene.tokenizer import LuceneTokenizer -class LuceneParser(Parser): +class LuceneQueryParser(PlatformQueryParser): tokenizer = LuceneTokenizer() log_source_pattern = r"___source_type___\s*(?:[:=])\s*(?:\"?(?P[%a-zA-Z_*:0-9\-/]+)\"|(?P[%a-zA-Z_*:0-9\-/]+))(?:\s+(?:and|or)\s+|\s+)?" # noqa: E501 log_source_key_types = ("index", "event\.category") - def _parse_log_sources(self, query: str) -> tuple[str, dict[str, list[str]]]: + def _parse_query(self, query: str) -> tuple[str, dict[str, list[str]]]: log_sources = {} for source_type in self.log_source_key_types: pattern = self.log_source_pattern.replace("___source_type___", source_type) @@ -43,17 +43,9 @@ def _parse_log_sources(self, query: str) -> tuple[str, dict[str, list[str]]]: return query, log_sources - @staticmethod - def _get_meta_info(source_mapping_ids: list[str], meta_info: dict) -> MetaInfoContainer: # noqa: ARG004 - return MetaInfoContainer(source_mapping_ids=source_mapping_ids) - - def _parse_query(self, query: str) -> tuple[str, dict[str, list[str]]]: - return self._parse_log_sources(query) - - def parse(self, text: str) -> SiemContainer: - query, log_sources = self._parse_query(text) + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: + query, log_sources = self._parse_query(raw_query_container.query) tokens, source_mappings = self.get_tokens_and_source_mappings(query, log_sources) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info([source_mapping.source_id for source_mapping in source_mappings], {}), - ) + meta_info = raw_query_container.meta_info + meta_info.source_mapping_ids = [source_mapping.source_id for source_mapping in source_mappings] + return TokenizedQueryContainer(tokens=tokens, meta_info=meta_info) diff --git a/translator/app/translator/platforms/base/lucene/renders/lucene.py b/translator/app/translator/platforms/base/lucene/renders/lucene.py index 82450b18..f06a9c7a 100644 --- a/translator/app/translator/platforms/base/lucene/renders/lucene.py +++ b/translator/app/translator/platforms/base/lucene/renders/lucene.py @@ -16,11 +16,12 @@ limitations under the License. ----------------------------------------------------------------- """ + 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.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.core.str_value_manager import StrValue from app.translator.platforms.base.lucene.mapping import LuceneLogSourceSignature from app.translator.platforms.base.lucene.str_value_manager import lucene_str_value_manager @@ -125,7 +126,7 @@ def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: return f"*{self._pre_process_value(field, value)}*" -class LuceneQueryRender(BaseQueryRender): +class LuceneQueryRender(PlatformQueryRender): or_token = "OR" and_token = "AND" not_token = "NOT" diff --git a/translator/app/translator/platforms/base/lucene/tokenizer.py b/translator/app/translator/platforms/base/lucene/tokenizer.py index b5bde101..0a0f902d 100644 --- a/translator/app/translator/platforms/base/lucene/tokenizer.py +++ b/translator/app/translator/platforms/base/lucene/tokenizer.py @@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ----------------------------------------------------------------- """ + import re from typing import ClassVar, Union diff --git a/translator/app/translator/platforms/base/spl/parsers/spl.py b/translator/app/translator/platforms/base/spl/parsers/spl.py index 21d055bb..9270defc 100644 --- a/translator/app/translator/platforms/base/spl/parsers/spl.py +++ b/translator/app/translator/platforms/base/spl/parsers/spl.py @@ -17,15 +17,14 @@ """ import re -from typing import Optional from app.translator.core.models.functions.base import ParsedFunctions -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser from app.translator.platforms.base.spl.tokenizer import SplTokenizer -class SplParser(Parser): +class SplQueryParser(PlatformQueryParser): log_source_pattern = r"^___source_type___\s*=\s*(?:\"(?P[%a-zA-Z_*:0-9\-/]+)\"|(?P[%a-zA-Z_*:0-9\-/]+))(?:\s+(?:and|or)\s+|\s+)?" # noqa: E501 log_source_key_types = ("index", "source", "sourcetype", "sourcecategory") @@ -47,19 +46,16 @@ def _parse_log_sources(self, query: str) -> tuple[dict[str, list[str]], str]: return log_sources, query - def _parse_query(self, query: str) -> tuple[dict[str, list[str]], ParsedFunctions, str]: + def _parse_query(self, query: str) -> tuple[str, dict[str, list[str]], ParsedFunctions]: query = query.strip() log_sources, query = self._parse_log_sources(query) query, functions = self.platform_functions.parse(query) - return log_sources, functions, query + return query, log_sources, functions - @staticmethod - def _get_meta_info(source_mapping_ids: list[str], meta_info: Optional[dict]) -> MetaInfoContainer: # noqa: ARG004 - return MetaInfoContainer(source_mapping_ids=source_mapping_ids) - - def parse(self, text: str) -> SiemContainer: - log_sources, functions, query = self._parse_query(text) + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: + query, log_sources, functions = self._parse_query(raw_query_container.query) tokens, source_mappings = self.get_tokens_and_source_mappings(query, log_sources) self.set_functions_fields_generic_names(functions=functions, source_mappings=source_mappings) - meta_info = self._get_meta_info([source_mapping.source_id for source_mapping in source_mappings], {}) - return SiemContainer(query=tokens, meta_info=meta_info, functions=functions) + meta_info = raw_query_container.meta_info + meta_info.source_mapping_ids = [source_mapping.source_id for source_mapping in source_mappings] + return TokenizedQueryContainer(tokens=tokens, meta_info=meta_info, functions=functions) diff --git a/translator/app/translator/platforms/base/spl/renders/spl.py b/translator/app/translator/platforms/base/spl/renders/spl.py index c449439f..d5b71ff1 100644 --- a/translator/app/translator/platforms/base/spl/renders/spl.py +++ b/translator/app/translator/platforms/base/spl/renders/spl.py @@ -16,11 +16,12 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Union from app.translator.const import DEFAULT_VALUE_TYPE from app.translator.core.exceptions.render import UnsupportedRenderMethod -from app.translator.core.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.platforms.base.spl.escape_manager import spl_escape_manager @@ -73,7 +74,7 @@ def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: raise UnsupportedRenderMethod(platform_name=self.details.name, method="Regex Expression") -class SplQueryRender(BaseQueryRender): +class SplQueryRender(PlatformQueryRender): or_token = "OR" and_token = "AND" not_token = "NOT" diff --git a/translator/app/translator/platforms/chronicle/mapping.py b/translator/app/translator/platforms/chronicle/mapping.py index bea60c0e..b26d137d 100644 --- a/translator/app/translator/platforms/chronicle/mapping.py +++ b/translator/app/translator/platforms/chronicle/mapping.py @@ -10,8 +10,7 @@ def __str__(self) -> str: class ChronicleMappings(BasePlatformMappings): - def prepare_log_source_signature(self, mapping: dict) -> ChronicleLogSourceSignature: - ... + def prepare_log_source_signature(self, mapping: dict) -> ChronicleLogSourceSignature: ... def get_suitable_source_mappings(self, field_names: list[str]) -> list[SourceMapping]: suitable_source_mappings = [] diff --git a/translator/app/translator/platforms/chronicle/parsers/chronicle.py b/translator/app/translator/platforms/chronicle/parsers/chronicle.py index e64c07b2..30767534 100644 --- a/translator/app/translator/platforms/chronicle/parsers/chronicle.py +++ b/translator/app/translator/platforms/chronicle/parsers/chronicle.py @@ -16,26 +16,21 @@ ----------------------------------------------------------------- """ - -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser from app.translator.platforms.chronicle.const import chronicle_query_details from app.translator.platforms.chronicle.mapping import ChronicleMappings, chronicle_mappings from app.translator.platforms.chronicle.tokenizer import ChronicleQueryTokenizer -class ChronicleParser(Parser): +class ChronicleQueryParser(PlatformQueryParser): mappings: ChronicleMappings = chronicle_mappings tokenizer: ChronicleQueryTokenizer = ChronicleQueryTokenizer() details: PlatformDetails = chronicle_query_details - def _get_meta_info(self, source_mapping_ids: list[str]) -> MetaInfoContainer: - return MetaInfoContainer(source_mapping_ids=source_mapping_ids) - - def parse(self, text: str) -> SiemContainer: - tokens, source_mappings = self.get_tokens_and_source_mappings(text, {}) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info([source_mapping.source_id for source_mapping in source_mappings]), - ) + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: + tokens, source_mappings = self.get_tokens_and_source_mappings(raw_query_container.query, {}) + meta_info = raw_query_container.meta_info + meta_info.source_mapping_ids = [source_mapping.source_id for source_mapping in source_mappings] + return TokenizedQueryContainer(tokens=tokens, meta_info=meta_info) diff --git a/translator/app/translator/platforms/chronicle/parsers/chronicle_rule.py b/translator/app/translator/platforms/chronicle/parsers/chronicle_rule.py index 635d16ac..88319fc9 100644 --- a/translator/app/translator/platforms/chronicle/parsers/chronicle_rule.py +++ b/translator/app/translator/platforms/chronicle/parsers/chronicle_rule.py @@ -19,80 +19,70 @@ import re from app.translator.core.exceptions.parser import TokenizerGeneralException -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import MetaInfoContainer, RawQueryContainer from app.translator.platforms.chronicle.const import chronicle_rule_details from app.translator.platforms.chronicle.mapping import ChronicleMappings, chronicle_mappings +from app.translator.platforms.chronicle.parsers.chronicle import ChronicleQueryParser from app.translator.platforms.chronicle.tokenizer import ChronicleRuleTokenizer -class ChronicleRuleParser(Parser): +class ChronicleRuleParser(ChronicleQueryParser): details: PlatformDetails = chronicle_rule_details rule_name_pattern = "rule\s(?P[a-z0-9_]+)\s{" meta_info_pattern = "meta:\n(?P[a-zA-Z0-9_\\\.*,>–<—~#$’`:;%+^\|?!@\s\"/=\-&'\(\)\[\]]+)\n\s+events:" # noqa: RUF001 - rule_pattern = "events:\n\s*(?P[a-zA-Z\w0-9_%{}\|\.,!#^><:~\s\"\/=+?\-–&;$()`\*@\[\]'\\\]+)\n\s+condition:" # noqa: RUF001 + rule_pattern = "events:\n\s*(?P[a-zA-Z\w0-9_%{}\|\.,!#^><:~\s\"\/=+?\-–&;$()`\*@\[\]'\\\]+)\n\s+condition:" # noqa: RUF001 event_name_pattern = "condition:\n\s*(?P\$[a-zA-Z_0-9]+)\n" mappings: ChronicleMappings = chronicle_mappings tokenizer = ChronicleRuleTokenizer() - def __parse_rule(self, rule: str) -> dict: - rule_name_search = re.search(self.rule_name_pattern, rule) - if rule_name_search is None: - raise TokenizerGeneralException(error="Field couldn't be found in rule.") + def __parse_rule(self, rule: str) -> tuple[str, str, str]: + if (rule_name_search := re.search(self.rule_name_pattern, rule)) is None: + raise TokenizerGeneralException(error="Rule name couldn't be found in rule.") rule_name = rule_name_search.group("rule_name") - meta_info_search = re.search(self.meta_info_pattern, rule) - if meta_info_search is None: + if (meta_info_search := re.search(self.meta_info_pattern, rule)) is None: raise TokenizerGeneralException(error="Rule meta info couldn't be found in rule.") meta_info = meta_info_search.group("meta_info") - query_search = re.search(self.rule_pattern, rule) - if query_search is None: + if (query_search := re.search(self.rule_pattern, rule)) is None: raise TokenizerGeneralException(error="Query couldn't be found in rule.") - query = query_search.group("rule") + query = query_search.group("query") - event_name_search = re.search(self.event_name_pattern, rule) - if query_search is None: + if (event_name_search := re.search(self.event_name_pattern, rule)) is None: raise TokenizerGeneralException(error="Event name couldn't be found in rule.") event_name = event_name_search.group("event_name") query = query.replace(f"{event_name}.", "") - return {"query": query.strip(" ").strip("\n"), "rule_name": rule_name, "meta_info": meta_info} + return query.strip(" ").strip("\n"), rule_name, meta_info @staticmethod def __prepare_title(name: str) -> str: return " ".join(name.split("_")).title() - def _get_meta_info(self, rule_name: str, source_mapping_ids: list[str], meta_info: str) -> MetaInfoContainer: + @staticmethod + def __parse_meta_info(meta_info_str: str) -> tuple[str, list[str], list[str]]: references = tags = [] description = None - for info in meta_info.strip(" ").strip("\n").split("\n"): + for info in meta_info_str.strip(" ").strip("\n").split("\n"): key, value = info.split(" = ") key = key.strip(" ") - if key == "reference": - references = [value.strip(" ").strip('"')] - elif key == "description": + if key == "description": description = value.strip(" ") + elif key == "reference": + references = [value.strip(" ").strip('"')] elif key == "tags": tags = [i.strip(" ").strip('"') for i in value.split(",")] - return MetaInfoContainer( - title=self.__prepare_title(rule_name), - source_mapping_ids=source_mapping_ids, - references=references, - tags=tags, - description=description, - ) + return description, references, tags - def parse(self, text: str) -> SiemContainer: - parsed_rule = self.__parse_rule(text) - tokens, source_mappings = self.get_tokens_and_source_mappings(parsed_rule.get("query"), {}) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info( - rule_name=parsed_rule.get("rule_name"), - meta_info=parsed_rule.get("meta_info"), - source_mapping_ids=[source_mapping.source_id for source_mapping in source_mappings], + def parse_raw_query(self, text: str, language: str) -> RawQueryContainer: + query, rule_name, meta_info_str = self.__parse_rule(text) + description, references, tags = self.__parse_meta_info(meta_info_str) + return RawQueryContainer( + query=query, + language=language, + meta_info=MetaInfoContainer( + title=self.__prepare_title(rule_name), description=description, references=references, tags=tags ), ) diff --git a/translator/app/translator/platforms/chronicle/renders/chronicle.py b/translator/app/translator/platforms/chronicle/renders/chronicle.py index b4a5e993..fdc02aa0 100644 --- a/translator/app/translator/platforms/chronicle/renders/chronicle.py +++ b/translator/app/translator/platforms/chronicle/renders/chronicle.py @@ -16,13 +16,14 @@ limitations under the License. ----------------------------------------------------------------- """ + 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.exceptions.render import UnsupportedRenderMethod from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.platforms.chronicle.const import chronicle_query_details from app.translator.platforms.chronicle.escape_manager import chronicle_escape_manager from app.translator.platforms.chronicle.mapping import ChronicleMappings, chronicle_mappings @@ -96,7 +97,7 @@ def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG00 raise UnsupportedRenderMethod(platform_name=self.details.name, method="Keywords") -class ChronicleQueryRender(BaseQueryRender): +class ChronicleQueryRender(PlatformQueryRender): details: PlatformDetails = chronicle_query_details mappings: ChronicleMappings = chronicle_mappings diff --git a/translator/app/translator/platforms/chronicle/renders/chronicle_rule.py b/translator/app/translator/platforms/chronicle/renders/chronicle_rule.py index 5967de4a..f81005c3 100644 --- a/translator/app/translator/platforms/chronicle/renders/chronicle_rule.py +++ b/translator/app/translator/platforms/chronicle/renders/chronicle_rule.py @@ -22,8 +22,8 @@ from app.translator.const import DEFAULT_VALUE_TYPE from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.chronicle.const import DEFAULT_CHRONICLE_SECURITY_RULE, chronicle_rule_details from app.translator.platforms.chronicle.renders.chronicle import ChronicleFieldValue, ChronicleQueryRender diff --git a/translator/app/translator/platforms/crowdstrike/parsers/crowdstrike.py b/translator/app/translator/platforms/crowdstrike/parsers/crowdstrike.py index fc34b670..08aabb12 100644 --- a/translator/app/translator/platforms/crowdstrike/parsers/crowdstrike.py +++ b/translator/app/translator/platforms/crowdstrike/parsers/crowdstrike.py @@ -15,14 +15,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ----------------------------------------------------------------- """ + from app.translator.core.models.platform_details import PlatformDetails -from app.translator.platforms.base.spl.parsers.spl import SplParser +from app.translator.platforms.base.spl.parsers.spl import SplQueryParser from app.translator.platforms.crowdstrike.const import crowdstrike_query_details from app.translator.platforms.crowdstrike.functions import CrowdStrikeFunctions, crowd_strike_functions from app.translator.platforms.crowdstrike.mapping import CrowdstrikeMappings, crowdstrike_mappings -class CrowdStrikeParser(SplParser): +class CrowdStrikeQueryParser(SplQueryParser): details: PlatformDetails = crowdstrike_query_details log_source_pattern = r"___source_type___\s*=\s*(?:\"(?P[%a-zA-Z_*:0-9\-/]+)\"|(?P[%a-zA-Z_*:0-9\-/]+))(?:\s+(?:and|or)\s+|\s+)?" # noqa: E501 diff --git a/translator/app/translator/platforms/crowdstrike/renders/crowdstrike.py b/translator/app/translator/platforms/crowdstrike/renders/crowdstrike.py index 5cda71f8..6c1c2e6c 100644 --- a/translator/app/translator/platforms/crowdstrike/renders/crowdstrike.py +++ b/translator/app/translator/platforms/crowdstrike/renders/crowdstrike.py @@ -16,6 +16,7 @@ limitations under the License. ----------------------------------------------------------------- """ + from app.translator.core.models.platform_details import PlatformDetails from app.translator.platforms.base.spl.renders.spl import SplFieldValue, SplQueryRender from app.translator.platforms.crowdstrike.const import crowdstrike_query_details diff --git a/translator/app/translator/platforms/elasticsearch/const.py b/translator/app/translator/platforms/elasticsearch/const.py index 4b811ed2..24c15a0b 100644 --- a/translator/app/translator/platforms/elasticsearch/const.py +++ b/translator/app/translator/platforms/elasticsearch/const.py @@ -3,15 +3,29 @@ PLATFORM_DETAILS = {"group_id": "elk stack", "group_name": "Elastic Stack", "alt_platform_name": "ECS"} +_ELASTIC_LUCENE_QUERY = "elastic-lucene-query" +_ELASTIC_LUCENE_RULE = "elastic-lucene-rule" +_ELASTIC_KIBANA_RULE = "elastic-kibana-rule" +_ELASTALERT_LUCENE_RULE = "elastalert-lucene-rule" +_ELASTIC_WATCHER_RULE = "elastic-watcher-rule" + +ELASTIC_QUERY_TYPES = { + _ELASTIC_LUCENE_QUERY, + _ELASTIC_LUCENE_RULE, + _ELASTIC_KIBANA_RULE, + _ELASTALERT_LUCENE_RULE, + _ELASTIC_WATCHER_RULE, +} + ELASTICSEARCH_LUCENE_QUERY_DETAILS = { - "siem_type": "elastic-lucene-query", + "siem_type": _ELASTIC_LUCENE_QUERY, "name": "Elasticsearch Query", "platform_name": "Query (Lucene)", **PLATFORM_DETAILS, } ELASTICSEARCH_RULE_DETAILS = { - "siem_type": "elastic-lucene-rule", + "siem_type": _ELASTIC_LUCENE_RULE, "name": "Elastic Rule", "platform_name": "Detection Rule (Lucene)", "first_choice": 0, @@ -19,7 +33,7 @@ } KIBANA_DETAILS = { - "siem_type": "elastic-kibana-rule", + "siem_type": _ELASTIC_KIBANA_RULE, "name": "Elastic Kibana Saved Search", "platform_name": "Kibana SavedSearch (JSON)", "first_choice": 0, @@ -27,7 +41,7 @@ } ELASTALERT_DETAILS = { - "siem_type": "elastalert-lucene-rule", + "siem_type": _ELASTALERT_LUCENE_RULE, "name": "ElastAlert", "platform_name": "Alert (Lucene)", "group_name": "ElastAlert", @@ -35,7 +49,7 @@ } XPACK_WATCHER_DETAILS = { - "siem_type": "elastic-watcher-rule", + "siem_type": _ELASTIC_WATCHER_RULE, "name": "Elastic Watcher", "platform_name": "Rule (Watcher)", "first_choice": 0, diff --git a/translator/app/translator/platforms/elasticsearch/parsers/detection_rule.py b/translator/app/translator/platforms/elasticsearch/parsers/detection_rule.py index 316ab577..229cba4b 100644 --- a/translator/app/translator/platforms/elasticsearch/parsers/detection_rule.py +++ b/translator/app/translator/platforms/elasticsearch/parsers/detection_rule.py @@ -16,37 +16,20 @@ ----------------------------------------------------------------- """ - from app.translator.core.mixins.rule import JsonRuleMixin -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer, RawQueryContainer from app.translator.platforms.elasticsearch.const import elasticsearch_rule_details -from app.translator.platforms.elasticsearch.parsers.elasticsearch import ElasticSearchParser +from app.translator.platforms.elasticsearch.parsers.elasticsearch import ElasticSearchQueryParser -class ElasticSearchRuleParser(ElasticSearchParser, JsonRuleMixin): +class ElasticSearchRuleParser(ElasticSearchQueryParser, JsonRuleMixin): details: PlatformDetails = elasticsearch_rule_details - def _parse_rule(self, text: str) -> dict: + def parse_raw_query(self, text: str, language: str) -> RawQueryContainer: rule = self.load_rule(text=text) - query = rule["query"] - description = rule["description"] - name = rule["name"] - query, logsources = self._parse_log_sources(query) - return {"query": query, "title": name, "description": description, "logsources": logsources} - - @staticmethod - def _get_meta_info(source_mapping_ids: list[str], meta_info: dict) -> MetaInfoContainer: - return MetaInfoContainer( - title=meta_info["title"], description=meta_info["description"], source_mapping_ids=source_mapping_ids - ) - - def parse(self, text: str) -> SiemContainer: - rule = self._parse_rule(text) - tokens, source_mappings = self.get_tokens_and_source_mappings(rule.get("query"), rule.get("log_sources", {})) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info( - source_mapping_ids=[source_mapping.source_id for source_mapping in source_mappings], meta_info=rule - ), + return RawQueryContainer( + query=rule["query"], + language=language, + meta_info=MetaInfoContainer(title=rule["name"], description=rule["description"]), ) diff --git a/translator/app/translator/platforms/elasticsearch/parsers/elasticsearch.py b/translator/app/translator/platforms/elasticsearch/parsers/elasticsearch.py index e51ebb32..88ae8ca0 100644 --- a/translator/app/translator/platforms/elasticsearch/parsers/elasticsearch.py +++ b/translator/app/translator/platforms/elasticsearch/parsers/elasticsearch.py @@ -17,11 +17,11 @@ """ from app.translator.core.models.platform_details import PlatformDetails -from app.translator.platforms.base.lucene.parsers.lucene import LuceneParser +from app.translator.platforms.base.lucene.parsers.lucene import LuceneQueryParser from app.translator.platforms.elasticsearch.const import elasticsearch_lucene_query_details from app.translator.platforms.elasticsearch.mapping import ElasticSearchMappings, elasticsearch_mappings -class ElasticSearchParser(LuceneParser): +class ElasticSearchQueryParser(LuceneQueryParser): details: PlatformDetails = elasticsearch_lucene_query_details mappings: ElasticSearchMappings = elasticsearch_mappings diff --git a/translator/app/translator/platforms/elasticsearch/renders/detection_rule.py b/translator/app/translator/platforms/elasticsearch/renders/detection_rule.py index 7211f9b9..ad8e7a94 100644 --- a/translator/app/translator/platforms/elasticsearch/renders/detection_rule.py +++ b/translator/app/translator/platforms/elasticsearch/renders/detection_rule.py @@ -24,8 +24,8 @@ from app.translator.core.mapping import SourceMapping from app.translator.core.mitre import MitreConfig -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.elasticsearch.const import ELASTICSEARCH_DETECTION_RULE, elasticsearch_rule_details from app.translator.platforms.elasticsearch.mapping import ElasticSearchMappings, elasticsearch_mappings from app.translator.platforms.elasticsearch.renders.elasticsearch import ( diff --git a/translator/app/translator/platforms/elasticsearch/renders/elast_alert.py b/translator/app/translator/platforms/elasticsearch/renders/elast_alert.py index 15c86efd..825c6713 100644 --- a/translator/app/translator/platforms/elasticsearch/renders/elast_alert.py +++ b/translator/app/translator/platforms/elasticsearch/renders/elast_alert.py @@ -16,12 +16,13 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Optional from app.translator.core.custom_types.meta_info import SeverityType from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.elasticsearch.const import ELASTICSEARCH_ALERT, elastalert_details from app.translator.platforms.elasticsearch.mapping import ElasticSearchMappings, elasticsearch_mappings from app.translator.platforms.elasticsearch.renders.elasticsearch import ( diff --git a/translator/app/translator/platforms/elasticsearch/renders/kibana.py b/translator/app/translator/platforms/elasticsearch/renders/kibana.py index af5b8438..54396562 100644 --- a/translator/app/translator/platforms/elasticsearch/renders/kibana.py +++ b/translator/app/translator/platforms/elasticsearch/renders/kibana.py @@ -16,14 +16,15 @@ limitations under the License. ----------------------------------------------------------------- """ + import copy from typing import Optional import ujson from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.elasticsearch.const import KIBANA_RULE, KIBANA_SEARCH_SOURCE_JSON, kibana_rule_details from app.translator.platforms.elasticsearch.mapping import ElasticSearchMappings, elasticsearch_mappings from app.translator.platforms.elasticsearch.renders.elasticsearch import ( diff --git a/translator/app/translator/platforms/elasticsearch/renders/xpack_watcher.py b/translator/app/translator/platforms/elasticsearch/renders/xpack_watcher.py index 099a480c..e099bad7 100644 --- a/translator/app/translator/platforms/elasticsearch/renders/xpack_watcher.py +++ b/translator/app/translator/platforms/elasticsearch/renders/xpack_watcher.py @@ -16,14 +16,15 @@ limitations under the License. ----------------------------------------------------------------- """ + import copy from typing import Optional import ujson from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.elasticsearch.const import XPACK_WATCHER_RULE, xpack_watcher_details from app.translator.platforms.elasticsearch.mapping import ElasticSearchMappings, elasticsearch_mappings from app.translator.platforms.elasticsearch.renders.elasticsearch import ( diff --git a/translator/app/translator/platforms/elasticsearch/tokenizer.py b/translator/app/translator/platforms/elasticsearch/tokenizer.py index fec8fdd1..dd426e9c 100644 --- a/translator/app/translator/platforms/elasticsearch/tokenizer.py +++ b/translator/app/translator/platforms/elasticsearch/tokenizer.py @@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ----------------------------------------------------------------- """ + from app.translator.platforms.base.lucene.tokenizer import LuceneTokenizer diff --git a/translator/app/translator/platforms/forti_siem/renders/forti_siem_rule.py b/translator/app/translator/platforms/forti_siem/renders/forti_siem_rule.py index 40a5ba2e..5eddcbaa 100644 --- a/translator/app/translator/platforms/forti_siem/renders/forti_siem_rule.py +++ b/translator/app/translator/platforms/forti_siem/renders/forti_siem_rule.py @@ -24,11 +24,10 @@ from app.translator.core.exceptions.render import UnsupportedRenderMethod from app.translator.core.mapping import SourceMapping from app.translator.core.models.field import FieldValue -from app.translator.core.models.functions.base import ParsedFunctions from app.translator.core.models.identifier import Identifier -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.models.query_container import MetaInfoContainer, TokenizedQueryContainer +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.core.str_value_manager import StrValue from app.translator.platforms.forti_siem.const import ( FORTI_SIEM_RULE, @@ -122,7 +121,7 @@ def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG00 raise UnsupportedRenderMethod(platform_name=self.details.name, method="Keywords") -class FortiSiemRuleRender(BaseQueryRender): +class FortiSiemRuleRender(PlatformQueryRender): details: PlatformDetails = forti_siem_rule_details mappings: FortiSiemMappings = forti_siem_mappings @@ -135,13 +134,13 @@ class FortiSiemRuleRender(BaseQueryRender): field_value_map = FortiSiemFieldValue(or_token=or_token) - def generate(self, query: list, meta_info: MetaInfoContainer, functions: ParsedFunctions) -> str: + def _generate_from_tokenized_query_container(self, query_container: TokenizedQueryContainer) -> str: queries_map = {} - source_mappings = self._get_source_mappings(meta_info.source_mapping_ids) + source_mappings = self._get_source_mappings(query_container.meta_info.source_mapping_ids) for source_mapping in source_mappings: is_event_type_set = False - field_values = [token for token in query if isinstance(token, FieldValue)] + field_values = [token for token in query_container.tokens if isinstance(token, FieldValue)] mapped_fields_set = set() for field_value in field_values: mapped_fields = self.map_field(field_value.field, source_mapping) @@ -150,14 +149,14 @@ def generate(self, query: list, meta_info: MetaInfoContainer, functions: ParsedF is_event_type_set = True self.__update_event_type_values(field_value, source_mapping.source_id) - result = self.generate_query(query=query, source_mapping=source_mapping) + result = self.generate_query(tokens=query_container.tokens, source_mapping=source_mapping) prefix = "" if is_event_type_set else self.generate_prefix(source_mapping.log_source_signature) finalized_query = self.finalize_query( prefix=prefix, query=result, - functions=self.generate_functions(functions.functions, source_mapping), - not_supported_functions=functions.not_supported, - meta_info=meta_info, + functions=self.generate_functions(query_container.functions.functions, source_mapping), + not_supported_functions=query_container.functions.not_supported, + meta_info=query_container.meta_info, source_mapping=source_mapping, fields=mapped_fields_set, ) diff --git a/translator/app/translator/platforms/graylog/parsers/graylog.py b/translator/app/translator/platforms/graylog/parsers/graylog.py index 71424fd6..fe598800 100644 --- a/translator/app/translator/platforms/graylog/parsers/graylog.py +++ b/translator/app/translator/platforms/graylog/parsers/graylog.py @@ -17,11 +17,11 @@ """ from app.translator.core.models.platform_details import PlatformDetails -from app.translator.platforms.base.lucene.parsers.lucene import LuceneParser +from app.translator.platforms.base.lucene.parsers.lucene import LuceneQueryParser from app.translator.platforms.graylog.const import graylog_details from app.translator.platforms.graylog.mapping import GraylogMappings, graylog_mappings -class GraylogParser(LuceneParser): +class GraylogQueryParser(LuceneQueryParser): details: PlatformDetails = graylog_details mappings: GraylogMappings = graylog_mappings diff --git a/translator/app/translator/platforms/graylog/renders/graylog.py b/translator/app/translator/platforms/graylog/renders/graylog.py index 5d584b07..456a7889 100644 --- a/translator/app/translator/platforms/graylog/renders/graylog.py +++ b/translator/app/translator/platforms/graylog/renders/graylog.py @@ -27,7 +27,7 @@ class GraylogFieldValue(LuceneFieldValue): details: PlatformDetails = graylog_details -class GraylogRender(LuceneQueryRender): +class GraylogQueryRender(LuceneQueryRender): details: PlatformDetails = graylog_details mappings: GraylogMappings = graylog_mappings diff --git a/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_query.py b/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_query.py index d182f6da..e062dfb4 100644 --- a/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_query.py +++ b/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_query.py @@ -16,6 +16,7 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Union from app.translator.const import DEFAULT_VALUE_TYPE @@ -24,18 +25,16 @@ from app.translator.core.exceptions.render import BaseRenderException from app.translator.core.mapping import LogSourceSignature, SourceMapping from app.translator.core.models.field import FieldValue, Keyword -from app.translator.core.models.functions.base import ParsedFunctions from app.translator.core.models.identifier import Identifier -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.models.query_container import TokenizedQueryContainer +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.platforms.logrhythm_axon.const import UNMAPPED_FIELD_DEFAULT_NAME, logrhythm_axon_query_details from app.translator.platforms.logrhythm_axon.mapping import LogRhythmAxonMappings, logrhythm_axon_mappings from app.translator.platforms.microsoft.escape_manager import microsoft_escape_manager -class LogRhythmRegexRenderException(BaseRenderException): - ... +class LogRhythmRegexRenderException(BaseRenderException): ... class LogRhythmAxonFieldValue(BaseQueryFieldValue): @@ -191,7 +190,7 @@ def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: return f'{field} matches "{value}"' -class LogRhythmAxonQueryRender(BaseQueryRender): +class LogRhythmAxonQueryRender(PlatformQueryRender): details: PlatformDetails = logrhythm_axon_query_details or_token = "OR" @@ -241,25 +240,25 @@ def apply_token(self, token: Union[FieldValue, Keyword, Identifier], source_mapp return token.token_type - def generate(self, query: list, meta_info: MetaInfoContainer, functions: ParsedFunctions) -> str: + def _generate_from_tokenized_query_container(self, query_container: TokenizedQueryContainer) -> str: queries_map = {} - source_mappings = self._get_source_mappings(meta_info.source_mapping_ids) + source_mappings = self._get_source_mappings(query_container.meta_info.source_mapping_ids) for source_mapping in source_mappings: prefix = self.generate_prefix(source_mapping.log_source_signature) - if "product" in meta_info.parsed_logsources: - prefix = f"{prefix} CONTAINS {meta_info.parsed_logsources['product'][0]}" + if "product" in query_container.meta_info.parsed_logsources: + prefix = f"{prefix} CONTAINS {query_container.meta_info.parsed_logsources['product'][0]}" else: prefix = f"{prefix} CONTAINS anything" - result = self.generate_query(query=query, source_mapping=source_mapping) + result = self.generate_query(tokens=query_container.tokens, source_mapping=source_mapping) finalized_query = self.finalize_query( prefix=prefix, query=result, - functions=self.generate_functions(functions.functions, source_mapping), - not_supported_functions=functions.not_supported, - meta_info=meta_info, + functions=self.generate_functions(query_container.functions.functions, source_mapping), + not_supported_functions=query_container.functions.not_supported, + meta_info=query_container.meta_info, source_mapping=source_mapping, ) queries_map[source_mapping.source_id] = finalized_query diff --git a/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_rule.py b/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_rule.py index 014c9ebb..59a86013 100644 --- a/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_rule.py +++ b/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_rule.py @@ -16,14 +16,15 @@ limitations under the License. ----------------------------------------------------------------- """ + import copy import json from typing import Optional from app.translator.core.custom_types.meta_info import SeverityType from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.logrhythm_axon.const import DEFAULT_LOGRHYTHM_AXON_RULE, logrhythm_axon_rule_details from app.translator.platforms.logrhythm_axon.renders.logrhythm_axon_query import ( LogRhythmAxonFieldValue, diff --git a/translator/app/translator/platforms/logscale/functions/const.py b/translator/app/translator/platforms/logscale/functions/const.py index 904b3143..d9f1dee9 100644 --- a/translator/app/translator/platforms/logscale/functions/const.py +++ b/translator/app/translator/platforms/logscale/functions/const.py @@ -1,4 +1,3 @@ - from app.translator.tools.custom_enum import CustomEnum diff --git a/translator/app/translator/platforms/logscale/parsers/logscale.py b/translator/app/translator/platforms/logscale/parsers/logscale.py index 22e69242..1f2b5947 100644 --- a/translator/app/translator/platforms/logscale/parsers/logscale.py +++ b/translator/app/translator/platforms/logscale/parsers/logscale.py @@ -16,37 +16,30 @@ ----------------------------------------------------------------- """ - from app.translator.core.models.functions.base import ParsedFunctions -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser from app.translator.platforms.logscale.const import logscale_query_details from app.translator.platforms.logscale.functions import LogScaleFunctions, log_scale_functions from app.translator.platforms.logscale.mapping import LogScaleMappings, logscale_mappings from app.translator.platforms.logscale.tokenizer import LogScaleTokenizer -class LogScaleParser(Parser): +class LogScaleQueryParser(PlatformQueryParser): details: PlatformDetails = logscale_query_details platform_functions: LogScaleFunctions = log_scale_functions tokenizer = LogScaleTokenizer() mappings: LogScaleMappings = logscale_mappings - @staticmethod - def _get_meta_info(source_mapping_ids: list[str], metainfo: dict) -> MetaInfoContainer: # noqa: ARG004 - return MetaInfoContainer(source_mapping_ids=source_mapping_ids) - def _parse_query(self, query: str) -> tuple[str, ParsedFunctions]: - functions, query_str = self.platform_functions.parse(query) - return query_str, functions + functions, query = self.platform_functions.parse(query) + return query, functions - def parse(self, text: str) -> SiemContainer: - query, functions = self._parse_query(query=text) + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: + query, functions = self._parse_query(query=raw_query_container.query) tokens, source_mappings = self.get_tokens_and_source_mappings(query, {}) self.set_functions_fields_generic_names(functions=functions, source_mappings=source_mappings) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info([source_mapping.source_id for source_mapping in source_mappings], {}), - functions=functions, - ) + meta_info = raw_query_container.meta_info + meta_info.source_mapping_ids = [source_mapping.source_id for source_mapping in source_mappings] + return TokenizedQueryContainer(tokens=tokens, meta_info=meta_info, functions=functions) diff --git a/translator/app/translator/platforms/logscale/parsers/logscale_alert.py b/translator/app/translator/platforms/logscale/parsers/logscale_alert.py index 5e225746..52eb5ec8 100644 --- a/translator/app/translator/platforms/logscale/parsers/logscale_alert.py +++ b/translator/app/translator/platforms/logscale/parsers/logscale_alert.py @@ -16,39 +16,20 @@ ----------------------------------------------------------------- """ - from app.translator.core.mixins.rule import JsonRuleMixin -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer, RawQueryContainer from app.translator.platforms.logscale.const import logscale_alert_details -from app.translator.platforms.logscale.parsers.logscale import LogScaleParser +from app.translator.platforms.logscale.parsers.logscale import LogScaleQueryParser -class LogScaleAlertParser(LogScaleParser, JsonRuleMixin): +class LogScaleAlertParser(LogScaleQueryParser, JsonRuleMixin): details: PlatformDetails = logscale_alert_details - def _parse_rule(self, text: str) -> dict[str, str]: + def parse_raw_query(self, text: str, language: str) -> RawQueryContainer: rule = self.load_rule(text=text) - query = rule["query"]["queryString"] - description = rule["description"] - name = rule["name"] - return {"query": query, "name": name, "description": description} - - @staticmethod - def _get_meta_info(source_mapping_ids: list[str], meta_info: dict) -> MetaInfoContainer: - return MetaInfoContainer( - title=meta_info["name"], description=meta_info["description"], source_mapping_ids=source_mapping_ids - ) - - def parse(self, text: str) -> SiemContainer: - parsed_rule = self._parse_rule(text) - query, functions = self._parse_query(query=parsed_rule.pop("query")) - tokens, source_mappings = self.get_tokens_and_source_mappings(query, {}) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info( - meta_info=parsed_rule, - source_mapping_ids=[source_mapping.source_id for source_mapping in source_mappings], - ), - functions=functions, + return RawQueryContainer( + query=rule["query"]["queryString"], + language=language, + meta_info=MetaInfoContainer(title=rule["name"], description=rule["description"]), ) diff --git a/translator/app/translator/platforms/logscale/renders/logscale.py b/translator/app/translator/platforms/logscale/renders/logscale.py index a7639894..9f94808a 100644 --- a/translator/app/translator/platforms/logscale/renders/logscale.py +++ b/translator/app/translator/platforms/logscale/renders/logscale.py @@ -16,13 +16,14 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Optional, Union from app.translator.const import DEFAULT_VALUE_TYPE from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.models.query_container import MetaInfoContainer +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.platforms.logscale.const import logscale_query_details from app.translator.platforms.logscale.escape_manager import logscale_escape_manager from app.translator.platforms.logscale.functions import LogScaleFunctions, log_scale_functions @@ -90,7 +91,7 @@ def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: return f"/{self.apply_value(value)}/i" -class LogScaleQueryRender(BaseQueryRender): +class LogScaleQueryRender(PlatformQueryRender): details: PlatformDetails = logscale_query_details mappings: LogScaleMappings = logscale_mappings platform_functions: LogScaleFunctions = log_scale_functions diff --git a/translator/app/translator/platforms/logscale/renders/logscale_alert.py b/translator/app/translator/platforms/logscale/renders/logscale_alert.py index de575e8f..35050a83 100644 --- a/translator/app/translator/platforms/logscale/renders/logscale_alert.py +++ b/translator/app/translator/platforms/logscale/renders/logscale_alert.py @@ -16,13 +16,14 @@ limitations under the License. ----------------------------------------------------------------- """ + import copy import json from typing import Optional from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.logscale.const import DEFAULT_LOGSCALE_ALERT, logscale_alert_details from app.translator.platforms.logscale.renders.logscale import LogScaleFieldValue, LogScaleQueryRender from app.translator.tools.utils import get_rule_description_str diff --git a/translator/app/translator/platforms/microsoft/parsers/microsoft_defender.py b/translator/app/translator/platforms/microsoft/parsers/microsoft_defender.py index 4468713b..576db97c 100644 --- a/translator/app/translator/platforms/microsoft/parsers/microsoft_defender.py +++ b/translator/app/translator/platforms/microsoft/parsers/microsoft_defender.py @@ -20,10 +20,10 @@ from app.translator.platforms.microsoft.const import microsoft_defender_details from app.translator.platforms.microsoft.functions import MicrosoftFunctions, microsoft_defender_functions from app.translator.platforms.microsoft.mapping import MicrosoftDefenderMappings, microsoft_defender_mappings -from app.translator.platforms.microsoft.parsers.microsoft_sentinel import MicrosoftParser +from app.translator.platforms.microsoft.parsers.microsoft_sentinel import MicrosoftSentinelQueryParser -class MicrosoftDefenderQueryParser(MicrosoftParser): +class MicrosoftDefenderQueryParser(MicrosoftSentinelQueryParser): mappings: MicrosoftDefenderMappings = microsoft_defender_mappings details: PlatformDetails = microsoft_defender_details platform_functions: MicrosoftFunctions = microsoft_defender_functions diff --git a/translator/app/translator/platforms/microsoft/parsers/microsoft_sentinel.py b/translator/app/translator/platforms/microsoft/parsers/microsoft_sentinel.py index f07e7a45..49ba12fc 100644 --- a/translator/app/translator/platforms/microsoft/parsers/microsoft_sentinel.py +++ b/translator/app/translator/platforms/microsoft/parsers/microsoft_sentinel.py @@ -16,38 +16,31 @@ ----------------------------------------------------------------- """ - from app.translator.core.models.functions.base import ParsedFunctions -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser from app.translator.platforms.microsoft.const import microsoft_sentinel_query_details from app.translator.platforms.microsoft.functions import MicrosoftFunctions, microsoft_sentinel_functions from app.translator.platforms.microsoft.mapping import MicrosoftSentinelMappings, microsoft_sentinel_mappings from app.translator.platforms.microsoft.tokenizer import MicrosoftSentinelTokenizer -class MicrosoftParser(Parser): +class MicrosoftSentinelQueryParser(PlatformQueryParser): platform_functions: MicrosoftFunctions = microsoft_sentinel_functions mappings: MicrosoftSentinelMappings = microsoft_sentinel_mappings tokenizer = MicrosoftSentinelTokenizer() details: PlatformDetails = microsoft_sentinel_query_details - @staticmethod - def _get_meta_info(source_mapping_ids: list[str], meta_info: dict) -> MetaInfoContainer: # noqa: ARG004 - return MetaInfoContainer(source_mapping_ids=source_mapping_ids) - def _parse_query(self, query: str) -> tuple[str, dict[str, list[str]], ParsedFunctions]: table, query, functions = self.platform_functions.parse(query) log_sources = {"table": [table]} return query, log_sources, functions - def parse(self, text: str) -> SiemContainer: - query, log_sources, functions = self._parse_query(query=text) + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: + query, log_sources, functions = self._parse_query(query=raw_query_container.query) tokens, source_mappings = self.get_tokens_and_source_mappings(query, log_sources) self.set_functions_fields_generic_names(functions=functions, source_mappings=source_mappings) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info([source_mapping.source_id for source_mapping in source_mappings], {}), - functions=functions, - ) + meta_info = raw_query_container.meta_info + meta_info.source_mapping_ids = [source_mapping.source_id for source_mapping in source_mappings] + return TokenizedQueryContainer(tokens=tokens, meta_info=meta_info, functions=functions) diff --git a/translator/app/translator/platforms/microsoft/parsers/microsoft_sentinel_rule.py b/translator/app/translator/platforms/microsoft/parsers/microsoft_sentinel_rule.py index 208ac803..cde273a3 100644 --- a/translator/app/translator/platforms/microsoft/parsers/microsoft_sentinel_rule.py +++ b/translator/app/translator/platforms/microsoft/parsers/microsoft_sentinel_rule.py @@ -16,34 +16,20 @@ ----------------------------------------------------------------- """ - from app.translator.core.mixins.rule import JsonRuleMixin -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer, RawQueryContainer from app.translator.platforms.microsoft.const import microsoft_sentinel_rule_details -from app.translator.platforms.microsoft.parsers.microsoft_sentinel import MicrosoftParser +from app.translator.platforms.microsoft.parsers.microsoft_sentinel import MicrosoftSentinelQueryParser -class MicrosoftRuleParser(MicrosoftParser, JsonRuleMixin): +class MicrosoftSentinelRuleParser(MicrosoftSentinelQueryParser, JsonRuleMixin): details: PlatformDetails = microsoft_sentinel_rule_details - @staticmethod - def _get_meta_info(source_mapping_ids: list[str], meta_info: dict) -> MetaInfoContainer: - return MetaInfoContainer( - source_mapping_ids=source_mapping_ids, - title=meta_info.get("displayName"), - description=meta_info.get("description"), - ) - - def parse(self, text: str) -> SiemContainer: + def parse_raw_query(self, text: str, language: str) -> RawQueryContainer: rule = self.load_rule(text=text) - query, log_sources, functions = self._parse_query(query=rule.get("query")) - tokens, source_mappings = self.get_tokens_and_source_mappings(query, log_sources) - - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info( - source_mapping_ids=[source_mapping.source_id for source_mapping in source_mappings], meta_info=rule - ), - functions=functions, + return RawQueryContainer( + query=rule["query"], + language=language, + meta_info=MetaInfoContainer(title=rule.get("displayName"), description=rule.get("description")), ) diff --git a/translator/app/translator/platforms/microsoft/renders/microsoft_defender_cti.py b/translator/app/translator/platforms/microsoft/renders/microsoft_defender_cti.py index 504a7fda..bdb76743 100644 --- a/translator/app/translator/platforms/microsoft/renders/microsoft_defender_cti.py +++ b/translator/app/translator/platforms/microsoft/renders/microsoft_defender_cti.py @@ -16,6 +16,7 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import ClassVar from app.translator.core.models.platform_details import PlatformDetails diff --git a/translator/app/translator/platforms/microsoft/renders/microsoft_sentinel.py b/translator/app/translator/platforms/microsoft/renders/microsoft_sentinel.py index b45df389..64dd63b5 100644 --- a/translator/app/translator/platforms/microsoft/renders/microsoft_sentinel.py +++ b/translator/app/translator/platforms/microsoft/renders/microsoft_sentinel.py @@ -16,12 +16,13 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Union from app.translator.const import DEFAULT_VALUE_TYPE from app.translator.core.mapping import LogSourceSignature from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.platforms.microsoft.const import microsoft_sentinel_query_details from app.translator.platforms.microsoft.escape_manager import microsoft_escape_manager from app.translator.platforms.microsoft.functions import MicrosoftFunctions, microsoft_sentinel_functions @@ -101,7 +102,7 @@ def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: return f"* contains @'{self.__escape_value(value)}'" -class MicrosoftSentinelQueryRender(BaseQueryRender): +class MicrosoftSentinelQueryRender(PlatformQueryRender): details: PlatformDetails = microsoft_sentinel_query_details platform_functions: MicrosoftFunctions = microsoft_sentinel_functions diff --git a/translator/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py b/translator/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py index 9e5932d4..38c23a70 100644 --- a/translator/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py +++ b/translator/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py @@ -16,14 +16,15 @@ limitations under the License. ----------------------------------------------------------------- """ + import copy import json from typing import Optional from app.translator.core.custom_types.meta_info import SeverityType from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.microsoft.const import DEFAULT_MICROSOFT_SENTINEL_RULE, microsoft_sentinel_rule_details from app.translator.platforms.microsoft.renders.microsoft_sentinel import ( MicrosoftSentinelFieldValue, diff --git a/translator/app/translator/platforms/opensearch/parsers/opensearch.py b/translator/app/translator/platforms/opensearch/parsers/opensearch.py index 3c48ef95..76fc0752 100644 --- a/translator/app/translator/platforms/opensearch/parsers/opensearch.py +++ b/translator/app/translator/platforms/opensearch/parsers/opensearch.py @@ -17,11 +17,11 @@ """ from app.translator.core.models.platform_details import PlatformDetails -from app.translator.platforms.base.lucene.parsers.lucene import LuceneParser +from app.translator.platforms.base.lucene.parsers.lucene import LuceneQueryParser from app.translator.platforms.opensearch.const import opensearch_query_details from app.translator.platforms.opensearch.mapping import OpenSearchMappings, opensearch_mappings -class OpenSearchParser(LuceneParser): +class OpenSearchQueryParser(LuceneQueryParser): details: PlatformDetails = opensearch_query_details mappings: OpenSearchMappings = opensearch_mappings diff --git a/translator/app/translator/platforms/opensearch/renders/opensearch.py b/translator/app/translator/platforms/opensearch/renders/opensearch.py index f8103456..bbb3c5af 100644 --- a/translator/app/translator/platforms/opensearch/renders/opensearch.py +++ b/translator/app/translator/platforms/opensearch/renders/opensearch.py @@ -16,6 +16,7 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Union from app.translator.const import DEFAULT_VALUE_TYPE diff --git a/translator/app/translator/platforms/opensearch/renders/opensearch_rule.py b/translator/app/translator/platforms/opensearch/renders/opensearch_rule.py index f94c48c2..5a4e8531 100644 --- a/translator/app/translator/platforms/opensearch/renders/opensearch_rule.py +++ b/translator/app/translator/platforms/opensearch/renders/opensearch_rule.py @@ -16,6 +16,7 @@ limitations under the License. ----------------------------------------------------------------- """ + import copy from typing import Optional @@ -23,8 +24,8 @@ from app.translator.core.custom_types.meta_info import SeverityType from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.opensearch.const import OPENSEARCH_RULE, opensearch_rule_details from app.translator.platforms.opensearch.mapping import OpenSearchMappings, opensearch_mappings from app.translator.platforms.opensearch.renders.opensearch import OpenSearchFieldValue, OpenSearchQueryRender diff --git a/translator/app/translator/platforms/opensearch/tokenizer.py b/translator/app/translator/platforms/opensearch/tokenizer.py index 1cc67c6f..c07f47e2 100644 --- a/translator/app/translator/platforms/opensearch/tokenizer.py +++ b/translator/app/translator/platforms/opensearch/tokenizer.py @@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ----------------------------------------------------------------- """ + from app.translator.platforms.base.lucene.tokenizer import LuceneTokenizer diff --git a/translator/app/translator/platforms/qradar/escape_manager.py b/translator/app/translator/platforms/qradar/escape_manager.py index 206cf20e..81c4e630 100644 --- a/translator/app/translator/platforms/qradar/escape_manager.py +++ b/translator/app/translator/platforms/qradar/escape_manager.py @@ -1,8 +1,7 @@ from app.translator.core.escape_manager import EscapeManager -class QradarEscapeManager(EscapeManager): - ... +class QradarEscapeManager(EscapeManager): ... qradar_escape_manager = QradarEscapeManager() diff --git a/translator/app/translator/platforms/qradar/parsers/qradar.py b/translator/app/translator/platforms/qradar/parsers/qradar.py index ecd1bb3f..d7e63cd3 100644 --- a/translator/app/translator/platforms/qradar/parsers/qradar.py +++ b/translator/app/translator/platforms/qradar/parsers/qradar.py @@ -19,16 +19,16 @@ import re from typing import Union -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser from app.translator.platforms.qradar.const import NUM_VALUE_PATTERN, SINGLE_QUOTES_VALUE_PATTERN, qradar_query_details from app.translator.platforms.qradar.mapping import QradarMappings, qradar_mappings from app.translator.platforms.qradar.tokenizer import QradarTokenizer from app.translator.tools.utils import get_match_group -class QradarParser(Parser): +class QradarQueryParser(PlatformQueryParser): details: PlatformDetails = qradar_query_details tokenizer = QradarTokenizer() mappings: QradarMappings = qradar_mappings @@ -106,13 +106,9 @@ def _parse_query(self, text: str) -> tuple[str, dict[str, Union[list[str], list[ log_sources, query = self.__parse_log_sources(query) return query, log_sources - def _get_meta_info(self, source_mapping_ids: list[str]) -> MetaInfoContainer: - return MetaInfoContainer(source_mapping_ids=source_mapping_ids) - - def parse(self, text: str) -> SiemContainer: - query, log_sources = self._parse_query(text) + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: + query, log_sources = self._parse_query(raw_query_container.query) tokens, source_mappings = self.get_tokens_and_source_mappings(query, log_sources) - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info([source_mapping.source_id for source_mapping in source_mappings]), - ) + meta_info = raw_query_container.meta_info + meta_info.source_mapping_ids = [source_mapping.source_id for source_mapping in source_mappings] + return TokenizedQueryContainer(tokens=tokens, meta_info=meta_info) diff --git a/translator/app/translator/platforms/qradar/renders/qradar.py b/translator/app/translator/platforms/qradar/renders/qradar.py index 27d5635a..7685164e 100644 --- a/translator/app/translator/platforms/qradar/renders/qradar.py +++ b/translator/app/translator/platforms/qradar/renders/qradar.py @@ -16,6 +16,7 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Union from app.translator.const import DEFAULT_VALUE_TYPE @@ -23,7 +24,7 @@ from app.translator.core.mapping import SourceMapping from app.translator.core.models.functions.base import Function from app.translator.core.models.platform_details import PlatformDetails -from app.translator.core.render import BaseQueryFieldValue, BaseQueryRender +from app.translator.core.render import BaseQueryFieldValue, PlatformQueryRender from app.translator.platforms.qradar.const import qradar_query_details from app.translator.platforms.qradar.escape_manager import qradar_escape_manager from app.translator.platforms.qradar.mapping import QradarLogSourceSignature, QradarMappings, qradar_mappings @@ -108,7 +109,7 @@ def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: return f"UTF8(payload) ILIKE '%{self.apply_value(value)}%'" -class QradarQueryRender(BaseQueryRender): +class QradarQueryRender(PlatformQueryRender): details: PlatformDetails = qradar_query_details mappings: QradarMappings = qradar_mappings diff --git a/translator/app/translator/platforms/roota/parsers/roota.py b/translator/app/translator/platforms/roota/parsers/roota.py index fe239c30..2beee2d0 100644 --- a/translator/app/translator/platforms/roota/parsers/roota.py +++ b/translator/app/translator/platforms/roota/parsers/roota.py @@ -20,12 +20,12 @@ from app.translator.core.exceptions.core import RootARuleValidationException, UnsupportedRootAParser from app.translator.core.mixins.rule import YamlRuleMixin -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer -from app.translator.core.parser import Parser +from app.translator.core.models.query_container import MetaInfoContainer, RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser, QueryParser from app.translator.managers import parser_manager -class RootAParser(YamlRuleMixin): +class RootAParser(QueryParser, YamlRuleMixin): parsers = parser_manager mandatory_fields: ClassVar[set[str]] = { "name", @@ -38,7 +38,7 @@ class RootAParser(YamlRuleMixin): "license", } - def __update_meta_info(self, meta_info: MetaInfoContainer, rule: dict) -> MetaInfoContainer: + def __parse_meta_info(self, rule: dict) -> MetaInfoContainer: mitre_attack = rule.get("mitre-attack") or [] mitre_tags = [i.strip("") for i in mitre_attack.split(",")] if isinstance(mitre_attack, str) else mitre_attack mitre_attack = self.parse_mitre_attack(mitre_tags) @@ -47,33 +47,38 @@ def __update_meta_info(self, meta_info: MetaInfoContainer, rule: dict) -> MetaIn rule_tags = [i.strip() for i in rule_tags.split(",")] rule_tags += mitre_tags - meta_info.title = rule.get("name") - meta_info.description = rule.get("details") - meta_info.id = rule.get("uuid", meta_info.id) - meta_info.references = rule.get("references") - meta_info.license = rule.get("license", meta_info.license) - meta_info.tags = rule_tags or meta_info.tags - meta_info.mitre_attack = mitre_attack - meta_info.date = rule.get("date", meta_info.date) - meta_info.author = rule.get("author", meta_info.author) - meta_info.severity = rule.get("severity", meta_info.severity) - return meta_info + return MetaInfoContainer( + id_=rule.get("uuid"), + title=rule.get("name"), + description=rule.get("details"), + author=rule.get("author"), + date=rule.get("date"), + license_=rule.get("license"), + severity=rule.get("severity"), + references=rule.get("references"), + mitre_attack=mitre_attack, + tags=rule_tags, + ) - def _get_parser_class(self, parser: str) -> Parser: + def __get_parser_class(self, parser: str) -> PlatformQueryParser: parser_class = self.parsers.get(parser) if parser_class: return parser_class raise UnsupportedRootAParser(parser=parser) - def __validate_rule(self, rule: dict) -> None: + def parse_raw_query(self, text: str, language: str) -> RawQueryContainer: + rule = self.load_rule(text=text) if missing_fields := self.mandatory_fields.difference(set(rule.keys())): raise RootARuleValidationException(missing_fields=list(missing_fields)) - def parse(self, text: str) -> SiemContainer: - roota_rule = self.load_rule(text=text) - self.__validate_rule(rule=roota_rule) - detection = roota_rule.get("detection", {}).get("body", "") - parser = self._get_parser_class(roota_rule.get("detection", {}).get("language", "")) - siem_container = parser.parse(text=detection) - siem_container.meta_info = self.__update_meta_info(meta_info=siem_container.meta_info, rule=roota_rule) - return siem_container + detection = rule.get("detection", {}) + query = detection.get("body") + language = detection.get("language") + if not (query or language): + raise RootARuleValidationException(missing_fields=["detection"]) + + return RawQueryContainer(query=query, language=language, meta_info=self.__parse_meta_info(rule)) + + def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContainer: + parser = self.__get_parser_class(raw_query_container.language) + return parser.parse(raw_query_container) diff --git a/translator/app/translator/platforms/sigma/const.py b/translator/app/translator/platforms/sigma/const.py index 36811c1f..f5f4d75c 100644 --- a/translator/app/translator/platforms/sigma/const.py +++ b/translator/app/translator/platforms/sigma/const.py @@ -1,6 +1,8 @@ +SIGMA_SIEM_TYPE = "sigma" + SIGMA_RULE_DETAILS = { "name": "Sigma", - "siem_type": "sigma", + "siem_type": SIGMA_SIEM_TYPE, "platform_name": "Sigma", "group_name": "Sigma", "group_id": "sigma", diff --git a/translator/app/translator/platforms/sigma/parsers/sigma.py b/translator/app/translator/platforms/sigma/parsers/sigma.py index f2139e7c..c8a9b2a8 100644 --- a/translator/app/translator/platforms/sigma/parsers/sigma.py +++ b/translator/app/translator/platforms/sigma/parsers/sigma.py @@ -17,12 +17,13 @@ ----------------------------------------------------------------- """ + from typing import Union from app.translator.core.exceptions.core import SigmaRuleValidationException from app.translator.core.mixins.rule import YamlRuleMixin from app.translator.core.models.field import FieldValue, Field -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer +from app.translator.core.models.query_container import MetaInfoContainer, TokenizedQueryContainer from app.translator.core.models.platform_details import PlatformDetails from app.translator.core.tokenizer import QueryTokenizer from app.translator.platforms.sigma.const import SIGMA_RULE_DETAILS @@ -72,7 +73,7 @@ def __validate_rule(self, rule: dict): if missing_fields := self.mandatory_fields.difference(set(rule.keys())): raise SigmaRuleValidationException(missing_fields=list(missing_fields)) - def parse(self, text: str) -> SiemContainer: + def parse(self, text: str) -> TokenizedQueryContainer: sigma_rule = self.load_rule(text=text) self.__validate_rule(rule=sigma_rule) log_sources = { @@ -90,8 +91,8 @@ def parse(self, text: str) -> SiemContainer: sigma_fields_tokens = [Field(source_name=field) for field in sigma_fields] QueryTokenizer.set_field_tokens_generic_names_map(sigma_fields_tokens, source_mappings, self.mappings.default_mapping) - return SiemContainer( - query=tokens, + return TokenizedQueryContainer( + tokens=tokens, meta_info=self._get_meta_info( rule=sigma_rule, source_mapping_ids=[source_mapping.source_id for source_mapping in source_mappings], diff --git a/translator/app/translator/platforms/sigma/renders/sigma.py b/translator/app/translator/platforms/sigma/renders/sigma.py index d3ecb183..6594fdb4 100644 --- a/translator/app/translator/platforms/sigma/renders/sigma.py +++ b/translator/app/translator/platforms/sigma/renders/sigma.py @@ -16,7 +16,6 @@ ----------------------------------------------------------------- """ -import copy from typing import Any, Union import yaml @@ -26,9 +25,9 @@ from app.translator.core.custom_types.tokens import OperatorType from app.translator.core.mapping import DEFAULT_MAPPING_NAME, SourceMapping from app.translator.core.models.field import FieldValue, Keyword -from app.translator.core.models.functions.base import ParsedFunctions -from app.translator.core.models.parser_output import MetaInfoContainer +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.render import QueryRender from app.translator.core.str_value_manager import StrValue from app.translator.platforms.sigma.const import SIGMA_RULE_DETAILS from app.translator.platforms.sigma.mapping import SigmaLogSourceSignature, SigmaMappings, sigma_mappings @@ -38,7 +37,7 @@ from app.translator.platforms.sigma.str_value_manager import sigma_str_value_manager -class SigmaRender: +class SigmaRender(QueryRender): selection_name = "selection" selection_num = 0 keyword_name = "keyword" @@ -275,13 +274,16 @@ def __get_source_mapping(self, source_mapping_ids: list[str]) -> SourceMapping: return self.mappings.get_source_mapping(DEFAULT_MAPPING_NAME) - def generate(self, query, meta_info: MetaInfoContainer, functions: ParsedFunctions): + def _generate_from_raw_query_container(self, query_container: RawQueryContainer) -> str: + raise NotImplementedError + + def _generate_from_tokenized_query_container(self, query_container: TokenizedQueryContainer) -> str: self.reset_counters() + meta_info = query_container.meta_info source_mapping = self.__get_source_mapping(meta_info.source_mapping_ids) log_source_signature: SigmaLogSourceSignature = source_mapping.log_source_signature - sigma_condition = copy.deepcopy(query) - prepared_data_structure = DataStructureCompiler().generate(tokens=sigma_condition) + prepared_data_structure = DataStructureCompiler().generate(tokens=query_container.tokens) rule = { "title": meta_info.title or "Autogenerated Sigma Rule", @@ -298,3 +300,9 @@ def generate(self, query, meta_info: MetaInfoContainer, functions: ParsedFunctio "falsepositives": "", } return yaml.dump(rule, default_flow_style=False, sort_keys=False) + + def generate(self, query_container: Union[RawQueryContainer, TokenizedQueryContainer]) -> str: + if isinstance(query_container, RawQueryContainer): + return self._generate_from_raw_query_container(query_container) + + return self._generate_from_tokenized_query_container(query_container) diff --git a/translator/app/translator/platforms/splunk/parsers/splunk.py b/translator/app/translator/platforms/splunk/parsers/splunk.py index a3fa2ad2..02300eaf 100644 --- a/translator/app/translator/platforms/splunk/parsers/splunk.py +++ b/translator/app/translator/platforms/splunk/parsers/splunk.py @@ -17,13 +17,13 @@ """ from app.translator.core.models.platform_details import PlatformDetails -from app.translator.platforms.base.spl.parsers.spl import SplParser +from app.translator.platforms.base.spl.parsers.spl import SplQueryParser from app.translator.platforms.splunk.const import splunk_query_details from app.translator.platforms.splunk.functions import SplunkFunctions, splunk_functions from app.translator.platforms.splunk.mapping import SplunkMappings, splunk_mappings -class SplunkParser(SplParser): +class SplunkQueryParser(SplQueryParser): details: PlatformDetails = splunk_query_details log_source_pattern = r"^___source_type___\s*=\s*(?:\"(?P[%a-zA-Z_*:0-9\-/]+)\"|(?P[%a-zA-Z_*:0-9\-/]+))(?:\s+(?:and|or)\s+|\s+)?" # noqa: E501 diff --git a/translator/app/translator/platforms/splunk/parsers/splunk_alert.py b/translator/app/translator/platforms/splunk/parsers/splunk_alert.py index ee8476a1..9d874a3c 100644 --- a/translator/app/translator/platforms/splunk/parsers/splunk_alert.py +++ b/translator/app/translator/platforms/splunk/parsers/splunk_alert.py @@ -17,30 +17,17 @@ """ import re -from typing import Optional -from app.translator.core.models.parser_output import MetaInfoContainer, SiemContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer, RawQueryContainer from app.translator.platforms.splunk.const import splunk_alert_details -from app.translator.platforms.splunk.parsers.splunk import SplunkParser +from app.translator.platforms.splunk.parsers.splunk import SplunkQueryParser -class SplunkAlertParser(SplunkParser): +class SplunkAlertParser(SplunkQueryParser): details: PlatformDetails = splunk_alert_details - @staticmethod - def _get_meta_info(source_mapping_ids: list[str], meta_info: Optional[str]) -> MetaInfoContainer: - description = re.search(r"description\s*=\s*(?P.+)", meta_info).group() - description = description.replace("description = ", "") - return MetaInfoContainer(source_mapping_ids=source_mapping_ids, description=description) - - def parse(self, text: str) -> SiemContainer: + def parse_raw_query(self, text: str, language: str) -> RawQueryContainer: query = re.search(r"search\s*=\s*(?P.+)", text).group("query") - log_sources, functions, query = self._parse_query(query) - tokens, source_mappings = self.get_tokens_and_source_mappings(query, log_sources) - - return SiemContainer( - query=tokens, - meta_info=self._get_meta_info([source_mapping.source_id for source_mapping in source_mappings], text), - functions=functions, - ) + description = re.search(r"description\s*=\s*(?P.+)", text).group("description") + return RawQueryContainer(query=query, language=language, meta_info=MetaInfoContainer(description=description)) diff --git a/translator/app/translator/platforms/splunk/renders/splunk.py b/translator/app/translator/platforms/splunk/renders/splunk.py index 7e5e59c5..3a1c10ca 100644 --- a/translator/app/translator/platforms/splunk/renders/splunk.py +++ b/translator/app/translator/platforms/splunk/renders/splunk.py @@ -16,6 +16,7 @@ limitations under the License. ----------------------------------------------------------------- """ + from app.translator.core.models.platform_details import PlatformDetails from app.translator.platforms.base.spl.renders.spl import SplFieldValue, SplQueryRender from app.translator.platforms.splunk.const import splunk_query_details diff --git a/translator/app/translator/platforms/splunk/renders/splunk_alert.py b/translator/app/translator/platforms/splunk/renders/splunk_alert.py index c229feb7..2218ca42 100644 --- a/translator/app/translator/platforms/splunk/renders/splunk_alert.py +++ b/translator/app/translator/platforms/splunk/renders/splunk_alert.py @@ -16,12 +16,13 @@ limitations under the License. ----------------------------------------------------------------- """ + from typing import Optional from app.translator.core.custom_types.meta_info import SeverityType from app.translator.core.mapping import SourceMapping -from app.translator.core.models.parser_output import MetaInfoContainer from app.translator.core.models.platform_details import PlatformDetails +from app.translator.core.models.query_container import MetaInfoContainer from app.translator.platforms.splunk.const import DEFAULT_SPLUNK_ALERT, splunk_alert_details from app.translator.platforms.splunk.renders.splunk import SplunkFieldValue, SplunkQueryRender from app.translator.tools.utils import get_rule_description_str diff --git a/translator/app/translator/tools/decorators.py b/translator/app/translator/tools/decorators.py index 32e6f3f9..59e8f94c 100644 --- a/translator/app/translator/tools/decorators.py +++ b/translator/app/translator/tools/decorators.py @@ -1,4 +1,5 @@ from collections.abc import Callable +from typing import Any from app.translator.core.exceptions.core import BasePlatformException from app.translator.core.exceptions.iocs import BaseIOCsException @@ -6,8 +7,8 @@ from app.translator.core.exceptions.render import BaseRenderException -def handle_translation_exceptions(func: Callable[..., ...]) -> Callable[..., tuple[bool, str]]: - def exception_handler(*args, **kwargs) -> tuple[bool, str]: +def handle_translation_exceptions(func: Callable[..., ...]) -> Callable[..., tuple[bool, Any]]: + def exception_handler(*args, **kwargs) -> tuple[bool, Any]: try: result = func(*args, **kwargs) except (BaseParserException, BasePlatformException, BaseRenderException, BaseIOCsException) as err: diff --git a/translator/app/translator/translator.py b/translator/app/translator/translator.py index 75a48b31..8791d4c7 100644 --- a/translator/app/translator/translator.py +++ b/translator/app/translator/translator.py @@ -1,9 +1,14 @@ import logging +from typing import Optional, Union from app.translator.core.exceptions.core import UnsupportedPlatform -from app.translator.core.models.parser_output import SiemContainer +from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer +from app.translator.core.parser import PlatformQueryParser +from app.translator.core.render import QueryRender from app.translator.managers import ParserManager, RenderManager, parser_manager, render_manager +from app.translator.platforms.elasticsearch.const import ELASTIC_QUERY_TYPES from app.translator.platforms.roota.parsers.roota import RootAParser +from app.translator.platforms.sigma.parsers.sigma import SigmaParser from app.translator.tools.decorators import handle_translation_exceptions @@ -14,44 +19,80 @@ class SiemConverter: def __init__(self): self.logger = logging.getLogger("siem_converter") - @handle_translation_exceptions - def __parse_incoming_data(self, text: str, source: str) -> SiemContainer: + def __get_parser(self, source: str) -> Union[PlatformQueryParser, RootAParser, SigmaParser]: parser = RootAParser() if source == "roota" else self.parsers.get(source) if not parser: raise UnsupportedPlatform(platform=source, is_parser=True) - return parser.parse(text=text) - @handle_translation_exceptions - def __render_translation(self, parsed_data: SiemContainer, target: str) -> str: - render = self.renders.get(target) - if not render: + return parser + + def __get_render(self, target: str) -> QueryRender: + if not (render := self.renders.get(target)): raise UnsupportedPlatform(platform=target) - return render.generate( - query=parsed_data.query, meta_info=parsed_data.meta_info, functions=parsed_data.functions - ) - def __generate_one_translation(self, text: str, source: str, target: str) -> (bool, str): - status, parsed_data = self.__parse_incoming_data(text=text, source=source) - if status: - return self.__render_translation(parsed_data=parsed_data, target=target) - return status, parsed_data + return render + + @staticmethod + def __is_one_vendor_translation(source: str, target: str) -> bool: + vendors_query_types = [ELASTIC_QUERY_TYPES] + for vendor_query_types in vendors_query_types: + if source in vendor_query_types and target in vendor_query_types: + return True + + return False + + @handle_translation_exceptions + def __parse_incoming_data( + self, text: str, source: str, target: Optional[str] = None + ) -> tuple[Optional[RawQueryContainer], Optional[TokenizedQueryContainer]]: + parser = self.__get_parser(source) + if isinstance(parser, SigmaParser): + return None, parser.parse(text) + + raw_query_container = parser.parse_raw_query(text, language=source) + tokenized_query_container = None + if target and not self.__is_one_vendor_translation(raw_query_container.language, target): + tokenized_query_container = parser.parse(raw_query_container) + + return raw_query_container, tokenized_query_container + + @handle_translation_exceptions + def __render_translation( + self, query_container: Union[RawQueryContainer, TokenizedQueryContainer], target: str + ) -> str: + render = self.__get_render(target) + return render.generate(query_container) + + def __generate_one(self, text: str, source: str, target: str) -> (bool, str): + status, parsed_data = self.__parse_incoming_data(text=text, source=source, target=target) + if not status: + return status, parsed_data + + raw_query_container, tokenized_query_container = parsed_data + query_container = tokenized_query_container or raw_query_container + return self.__render_translation(query_container=query_container, target=target) def __generate_all(self, text: str, source: str) -> list[dict]: status, parsed_data = self.__parse_incoming_data(text=text, source=source) - if status: - result = [] - for target in self.renders.all_platforms(): - if target == source: - continue - translation = {"siem_type": target} - render_status, data = self.__render_translation(parsed_data=parsed_data, target=target) - translation.update({"status": render_status, "result": data}) - result.append(translation) - return result - return [{"status": status, "result": parsed_data, "siem_type": source}] + if not status: + return [{"status": status, "result": parsed_data, "siem_type": source}] + + raw_query_container, tokenized_query_container = parsed_data + result = [] + for target in self.renders.all_platforms(): + if target == source: + continue + + if raw_query_container and self.__is_one_vendor_translation(raw_query_container.language, target): + status, data = self.__render_translation(query_container=raw_query_container, target=target) + else: + status, data = self.__render_translation(query_container=tokenized_query_container, target=target) + result.append({"status": status, "result": parsed_data, "siem_type": target}) + + return result def generate_translation(self, text: str, source: str, target: str) -> (bool, str): - return self.__generate_one_translation(text=text, source=source, target=target) + return self.__generate_one(text=text, source=source, target=target) def generate_all_translation(self, text: str, source: str) -> list[dict]: return self.__generate_all(text=text, source=source) From 508ee10913edac4d9a0b7c37cfc911a3c2db8e05 Mon Sep 17 00:00:00 2001 From: Oleksandr Volha Date: Thu, 7 Mar 2024 16:35:41 +0200 Subject: [PATCH 2/4] refactoring --- .../app/translator/core/exceptions/core.py | 6 +- .../translator/core/exceptions/functions.py | 3 +- .../app/translator/core/exceptions/iocs.py | 6 +- .../app/translator/core/exceptions/parser.py | 3 +- .../app/translator/core/exceptions/render.py | 6 +- .../app/translator/core/str_value_manager.py | 63 ++++++++++++------- .../translator/platforms/chronicle/mapping.py | 3 +- .../renders/logrhythm_axon_query.py | 3 +- .../platforms/qradar/escape_manager.py | 3 +- 9 files changed, 64 insertions(+), 32 deletions(-) diff --git a/translator/app/translator/core/exceptions/core.py b/translator/app/translator/core/exceptions/core.py index 93d77a54..8f7d47fc 100644 --- a/translator/app/translator/core/exceptions/core.py +++ b/translator/app/translator/core/exceptions/core.py @@ -1,7 +1,9 @@ -class NotImplementedException(BaseException): ... +class NotImplementedException(BaseException): + ... -class BasePlatformException(BaseException): ... +class BasePlatformException(BaseException): + ... class StrictPlatformException(BasePlatformException): diff --git a/translator/app/translator/core/exceptions/functions.py b/translator/app/translator/core/exceptions/functions.py index 53c1a60f..17a956d3 100644 --- a/translator/app/translator/core/exceptions/functions.py +++ b/translator/app/translator/core/exceptions/functions.py @@ -1,4 +1,5 @@ -class BaseFunctionException(Exception): ... +class BaseFunctionException(Exception): + ... class InternalFunctionException(Exception): diff --git a/translator/app/translator/core/exceptions/iocs.py b/translator/app/translator/core/exceptions/iocs.py index 7c3966df..6ed9b988 100644 --- a/translator/app/translator/core/exceptions/iocs.py +++ b/translator/app/translator/core/exceptions/iocs.py @@ -1,7 +1,9 @@ -class BaseIOCsException(BaseException): ... +class BaseIOCsException(BaseException): + ... -class IocsLimitExceededException(BaseIOCsException): ... +class IocsLimitExceededException(BaseIOCsException): + ... class EmptyIOCSException(BaseIOCsException): diff --git a/translator/app/translator/core/exceptions/parser.py b/translator/app/translator/core/exceptions/parser.py index 0468bec7..6c9a8e08 100644 --- a/translator/app/translator/core/exceptions/parser.py +++ b/translator/app/translator/core/exceptions/parser.py @@ -1,4 +1,5 @@ -class BaseParserException(BaseException): ... +class BaseParserException(BaseException): + ... class TokenizerGeneralException(BaseParserException): diff --git a/translator/app/translator/core/exceptions/render.py b/translator/app/translator/core/exceptions/render.py index 8467f5c9..4dd14b35 100644 --- a/translator/app/translator/core/exceptions/render.py +++ b/translator/app/translator/core/exceptions/render.py @@ -1,4 +1,5 @@ -class BaseRenderException(BaseException): ... +class BaseRenderException(BaseException): + ... class UnexpectedLogsourceException(BaseRenderException): @@ -7,7 +8,8 @@ def __init__(self, platform_name: str, log_source: str): super().__init__(message) -class FunctionRenderException(BaseRenderException): ... +class FunctionRenderException(BaseRenderException): + ... class UnsupportedRenderMethod(BaseRenderException): diff --git a/translator/app/translator/core/str_value_manager.py b/translator/app/translator/core/str_value_manager.py index 355ebbbf..729a027a 100644 --- a/translator/app/translator/core/str_value_manager.py +++ b/translator/app/translator/core/str_value_manager.py @@ -22,70 +22,91 @@ from app.translator.core.escape_manager import EscapeManager -class BaseSpecSymbol: ... +class BaseSpecSymbol: + ... SpecSymbolType = TypeVar("SpecSymbolType", bound=BaseSpecSymbol) -class SingleSymbolWildCard(BaseSpecSymbol): ... +class SingleSymbolWildCard(BaseSpecSymbol): + ... -class UnboundLenWildCard(BaseSpecSymbol): ... +class UnboundLenWildCard(BaseSpecSymbol): + ... -class ReEndOfStrSymbol(BaseSpecSymbol): ... +class ReEndOfStrSymbol(BaseSpecSymbol): + ... -class ReWordSymbol(BaseSpecSymbol): ... +class ReWordSymbol(BaseSpecSymbol): + ... -class ReDigitalSymbol(BaseSpecSymbol): ... +class ReDigitalSymbol(BaseSpecSymbol): + ... -class ReAnySymbol(BaseSpecSymbol): ... +class ReAnySymbol(BaseSpecSymbol): + ... -class ReWhiteSpaceSymbol(BaseSpecSymbol): ... +class ReWhiteSpaceSymbol(BaseSpecSymbol): + ... -class ReOneOrMoreQuantifier(BaseSpecSymbol): ... +class ReOneOrMoreQuantifier(BaseSpecSymbol): + ... -class ReZeroOrMoreQuantifier(BaseSpecSymbol): ... +class ReZeroOrMoreQuantifier(BaseSpecSymbol): + ... -class ReZeroOrOneQuantifier(BaseSpecSymbol): ... +class ReZeroOrOneQuantifier(BaseSpecSymbol): + ... -class ReLeftParenthesis(BaseSpecSymbol): ... +class ReLeftParenthesis(BaseSpecSymbol): + ... -class ReRightParenthesis(BaseSpecSymbol): ... +class ReRightParenthesis(BaseSpecSymbol): + ... -class ReLeftSquareBracket(BaseSpecSymbol): ... +class ReLeftSquareBracket(BaseSpecSymbol): + ... -class ReRightSquareBracket(BaseSpecSymbol): ... +class ReRightSquareBracket(BaseSpecSymbol): + ... -class ReLeftCurlyBracket(BaseSpecSymbol): ... +class ReLeftCurlyBracket(BaseSpecSymbol): + ... -class ReRightCurlyBracket(BaseSpecSymbol): ... +class ReRightCurlyBracket(BaseSpecSymbol): + ... -class ReOrOperator(BaseSpecSymbol): ... +class ReOrOperator(BaseSpecSymbol): + ... -class ReCaretSymbol(BaseSpecSymbol): ... +class ReCaretSymbol(BaseSpecSymbol): + ... -class ReCommaSymbol(BaseSpecSymbol): ... +class ReCommaSymbol(BaseSpecSymbol): + ... -class ReHyphenSymbol(BaseSpecSymbol): ... +class ReHyphenSymbol(BaseSpecSymbol): + ... class StrValue(str): diff --git a/translator/app/translator/platforms/chronicle/mapping.py b/translator/app/translator/platforms/chronicle/mapping.py index b26d137d..bea60c0e 100644 --- a/translator/app/translator/platforms/chronicle/mapping.py +++ b/translator/app/translator/platforms/chronicle/mapping.py @@ -10,7 +10,8 @@ def __str__(self) -> str: class ChronicleMappings(BasePlatformMappings): - def prepare_log_source_signature(self, mapping: dict) -> ChronicleLogSourceSignature: ... + def prepare_log_source_signature(self, mapping: dict) -> ChronicleLogSourceSignature: + ... def get_suitable_source_mappings(self, field_names: list[str]) -> list[SourceMapping]: suitable_source_mappings = [] diff --git a/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_query.py b/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_query.py index e062dfb4..d6d34abc 100644 --- a/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_query.py +++ b/translator/app/translator/platforms/logrhythm_axon/renders/logrhythm_axon_query.py @@ -34,7 +34,8 @@ from app.translator.platforms.microsoft.escape_manager import microsoft_escape_manager -class LogRhythmRegexRenderException(BaseRenderException): ... +class LogRhythmRegexRenderException(BaseRenderException): + ... class LogRhythmAxonFieldValue(BaseQueryFieldValue): diff --git a/translator/app/translator/platforms/qradar/escape_manager.py b/translator/app/translator/platforms/qradar/escape_manager.py index 81c4e630..206cf20e 100644 --- a/translator/app/translator/platforms/qradar/escape_manager.py +++ b/translator/app/translator/platforms/qradar/escape_manager.py @@ -1,7 +1,8 @@ from app.translator.core.escape_manager import EscapeManager -class QradarEscapeManager(EscapeManager): ... +class QradarEscapeManager(EscapeManager): + ... qradar_escape_manager = QradarEscapeManager() From 02f92bd7f3204baefdf23091feeb3c9a8ff51d8e Mon Sep 17 00:00:00 2001 From: Oleksandr Volha Date: Thu, 7 Mar 2024 16:36:58 +0200 Subject: [PATCH 3/4] return licence --- translator/app/translator/core/functions.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/translator/app/translator/core/functions.py b/translator/app/translator/core/functions.py index db595235..5e4cefe5 100644 --- a/translator/app/translator/core/functions.py +++ b/translator/app/translator/core/functions.py @@ -1,3 +1,18 @@ +""" +Uncoder IO Commercial Edition License +----------------------------------------------------------------- +Copyright (c) 2023 SOC Prime, Inc. +This file is part of the Uncoder IO Commercial Edition ("CE") and is +licensed under the Uncoder IO Non-Commercial License (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + https://github.com/UncoderIO/UncoderIO/blob/main/LICENSE +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +----------------------------------------------------------------- +""" + from __future__ import annotations from abc import ABC, abstractmethod From 8e22eb58fe252b39f5a7f4b85f9144b8586a3549 Mon Sep 17 00:00:00 2001 From: Oleksandr Volha Date: Thu, 7 Mar 2024 16:37:29 +0200 Subject: [PATCH 4/4] return licence --- translator/app/translator/core/functions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/translator/app/translator/core/functions.py b/translator/app/translator/core/functions.py index 5e4cefe5..02f13657 100644 --- a/translator/app/translator/core/functions.py +++ b/translator/app/translator/core/functions.py @@ -2,11 +2,14 @@ Uncoder IO Commercial Edition License ----------------------------------------------------------------- Copyright (c) 2023 SOC Prime, Inc. + This file is part of the Uncoder IO Commercial Edition ("CE") and is licensed under the Uncoder IO Non-Commercial License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + https://github.com/UncoderIO/UncoderIO/blob/main/LICENSE + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy