Skip to content

Commit 6973aa3

Browse files
authored
Merge pull request #114 from UncoderIO/gis-7328
Created base platform: aql. And fixes for qradar
2 parents d3dba4e + 87274f8 commit 6973aa3

File tree

49 files changed

+308
-411
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+308
-411
lines changed

uncoder-core/app/translator/core/exceptions/core.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,7 @@ class InvalidYamlStructure(InvalidRuleStructure):
7777

7878
class InvalidJSONStructure(InvalidRuleStructure):
7979
rule_type: str = "JSON"
80+
81+
82+
class InvalidXMLStructure(InvalidRuleStructure):
83+
rule_type: str = "XML"

uncoder-core/app/translator/core/mixins/rule.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import json
2+
from typing import Union
23

4+
import xmltodict
35
import yaml
46

5-
from app.translator.core.exceptions.core import InvalidJSONStructure, InvalidYamlStructure
7+
from app.translator.core.exceptions.core import InvalidJSONStructure, InvalidXMLStructure, InvalidYamlStructure
68
from app.translator.core.mitre import MitreConfig
79

810

@@ -36,5 +38,13 @@ def parse_mitre_attack(self, tags: list[str]) -> dict[str, list]:
3638
result["techniques"].append(technique)
3739
elif tactic := self.mitre_config.get_tactic(tag):
3840
result["tactics"].append(tactic)
39-
4041
return result
42+
43+
44+
class XMLRuleMixin:
45+
@staticmethod
46+
def load_rule(text: Union[str, bytes]) -> dict:
47+
try:
48+
return xmltodict.parse(text)
49+
except Exception as err:
50+
raise InvalidXMLStructure(error=str(err)) from err

uncoder-core/app/translator/core/models/query_container.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ class RawQueryContainer:
5656
meta_info: MetaInfoContainer = field(default_factory=MetaInfoContainer)
5757

5858

59+
@dataclass
60+
class RawQueryDictContainer:
61+
query: dict
62+
language: str
63+
meta_info: dict
64+
65+
5966
@dataclass
6067
class TokenizedQueryContainer:
6168
tokens: list[TOKEN_TYPE]

uncoder-core/app/translator/core/parser.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@
3232

3333
class QueryParser(ABC):
3434
wrapped_with_comment_pattern: str = None
35+
details: PlatformDetails = None
3536

3637
def remove_comments(self, text: str) -> str:
37-
return re.sub(self.wrapped_with_comment_pattern, "\n", text, flags=re.MULTILINE).strip()
38+
if self.wrapped_with_comment_pattern:
39+
return re.sub(self.wrapped_with_comment_pattern, "\n", text, flags=re.MULTILINE).strip()
40+
41+
return text
3842

3943
def parse_raw_query(self, text: str, language: str) -> RawQueryContainer:
4044
return RawQueryContainer(query=text, language=language)
@@ -47,7 +51,6 @@ def parse(self, raw_query_container: RawQueryContainer) -> TokenizedQueryContain
4751
class PlatformQueryParser(QueryParser, ABC):
4852
mappings: BasePlatformMappings = None
4953
tokenizer: QueryTokenizer = None
50-
details: PlatformDetails = None
5154
platform_functions: PlatformFunctions = None
5255

5356
def get_fields_tokens(self, tokens: list[Union[FieldValue, Keyword, Identifier]]) -> list[Field]:

uncoder-core/app/translator/core/render_cti.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020

2121
from app.translator.core.models.iocs import IocsChunkValue
22+
from app.translator.core.models.platform_details import PlatformDetails
2223

2324

2425
class RenderCTI:
@@ -31,6 +32,7 @@ class RenderCTI:
3132
final_result_for_many: str = "union * | where ({result})\n"
3233
final_result_for_one: str = "union * | where {result}\n"
3334
default_mapping = None
35+
details: PlatformDetails = None
3436

3537
def create_field_value(self, field: str, value: str, generic_field: str) -> str: # noqa: ARG002
3638
return self.field_value_template.format(key=field, value=value)

uncoder-core/app/translator/core/tokenizer.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class QueryTokenizer(BaseTokenizer):
5252
single_value_operators_map: ClassVar[dict[str, str]] = {}
5353
# used to generate re pattern. so the keys order is important
5454
multi_value_operators_map: ClassVar[dict[str, str]] = {}
55+
# used to generate re pattern. so the keys order is important
56+
fields_operator_map: ClassVar[dict[str, str]] = {}
5557
operators_map: ClassVar[dict[str, str]] = {} # used to generate re pattern. so the keys order is important
5658

5759
logical_operator_pattern = r"^(?P<logical_operator>and|or|not|AND|OR|NOT)\s+"
@@ -73,7 +75,11 @@ class QueryTokenizer(BaseTokenizer):
7375
def __init_subclass__(cls, **kwargs):
7476
cls._validate_re_patterns()
7577
cls.value_pattern = cls.base_value_pattern.replace("___value_pattern___", cls._value_pattern)
76-
cls.operators_map = {**cls.single_value_operators_map, **cls.multi_value_operators_map}
78+
cls.operators_map = {
79+
**cls.single_value_operators_map,
80+
**cls.multi_value_operators_map,
81+
**cls.fields_operator_map,
82+
}
7783
cls.operator_pattern = rf"""(?:___field___\s*(?P<operator>(?:{'|'.join(cls.operators_map)})))\s*"""
7884

7985
@classmethod

uncoder-core/app/translator/managers.py

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
from abc import ABC
22
from functools import cached_property
3+
from typing import ClassVar, Union
34

45
from app.models.translation import TranslatorPlatform
5-
from app.translator.core.exceptions.core import UnsupportedRootAParser
6+
from app.translator.core.exceptions.core import UnsupportedPlatform, UnsupportedRootAParser
7+
from app.translator.core.parser import QueryParser
8+
from app.translator.core.render import QueryRender
9+
from app.translator.core.render_cti import RenderCTI
610

711

8-
class Manager(ABC):
9-
platforms = {}
10-
11-
def register(self, cls):
12-
self.platforms[cls.details.platform_id] = cls()
13-
return cls
14-
15-
def get(self, platform_id: str): # noqa: ANN201
16-
if platform := self.platforms.get(platform_id):
17-
return platform
18-
raise UnsupportedRootAParser(parser=platform_id)
12+
class PlatformManager(ABC):
13+
platforms: ClassVar[dict[str, Union[QueryParser, QueryRender, RenderCTI]]] = {}
1914

2015
def all_platforms(self) -> list:
2116
return list(self.platforms.keys())
@@ -40,54 +35,61 @@ def get_platforms_details(self) -> list[TranslatorPlatform]:
4035
return sorted(platforms, key=lambda platform: platform.group_name)
4136

4237

43-
class ParserManager(Manager):
44-
platforms = {}
45-
supported_by_roota_platforms = {}
46-
main_platforms = {}
38+
class ParserManager(PlatformManager):
39+
supported_by_roota_platforms: ClassVar[dict[str, QueryParser]] = {}
40+
main_platforms: ClassVar[dict[str, QueryParser]] = {}
4741

48-
def get_supported_by_roota(self, platform_id: str): # noqa: ANN201
42+
def get(self, platform_id: str) -> QueryParser:
43+
if platform := self.platforms.get(platform_id):
44+
return platform
45+
raise UnsupportedPlatform(platform=platform_id, is_parser=True)
46+
47+
def register(self, cls: type[QueryParser]) -> type[QueryParser]:
48+
self.platforms[cls.details.platform_id] = cls()
49+
return cls
50+
51+
def get_supported_by_roota(self, platform_id: str) -> QueryParser:
4952
if platform := self.supported_by_roota_platforms.get(platform_id):
5053
return platform
5154
raise UnsupportedRootAParser(parser=platform_id)
5255

53-
def register_supported_by_roota(self, cls):
56+
def register_supported_by_roota(self, cls: type[QueryParser]) -> type[QueryParser]:
5457
parser = cls()
5558
self.supported_by_roota_platforms[cls.details.platform_id] = parser
5659
self.platforms[cls.details.platform_id] = parser
5760
return cls
5861

59-
def register_main(self, cls):
62+
def register_main(self, cls: type[QueryParser]) -> type[QueryParser]:
6063
parser = cls()
6164
self.main_platforms[cls.details.platform_id] = parser
6265
self.platforms[cls.details.platform_id] = parser
6366
return cls
6467

65-
@cached_property
66-
def get_platforms_details(self) -> list[TranslatorPlatform]:
67-
platforms = [
68-
TranslatorPlatform(
69-
id=platform.details.platform_id,
70-
name=platform.details.name,
71-
code=platform.details.platform_id,
72-
group_name=platform.details.group_name,
73-
group_id=platform.details.group_id,
74-
platform_name=platform.details.platform_name,
75-
platform_id=platform.details.platform_id,
76-
alt_platform_name=platform.details.alt_platform_name,
77-
alt_platform=platform.details.alt_platform,
78-
first_choice=platform.details.first_choice,
79-
)
80-
for platform in self.platforms.values()
81-
]
82-
return sorted(platforms, key=lambda platform: platform.group_name)
8368

69+
class RenderManager(PlatformManager):
70+
platforms: ClassVar[dict[str, QueryRender]] = {}
71+
72+
def get(self, platform_id: str) -> QueryRender:
73+
if platform := self.platforms.get(platform_id):
74+
return platform
75+
raise UnsupportedPlatform(platform=platform_id)
76+
77+
def register(self, cls: type[QueryRender]) -> type[QueryRender]:
78+
self.platforms[cls.details.platform_id] = cls()
79+
return cls
8480

85-
class RenderManager(Manager):
86-
platforms = {}
8781

82+
class RenderCTIManager(PlatformManager):
83+
platforms: ClassVar[dict[str, RenderCTI]] = {}
8884

89-
class RenderCTIManager(Manager):
90-
platforms = {}
85+
def get(self, platform_id: str) -> RenderCTI:
86+
if platform := self.platforms.get(platform_id):
87+
return platform
88+
raise UnsupportedPlatform(platform=platform_id)
89+
90+
def register(self, cls: type[RenderCTI]) -> type[RenderCTI]:
91+
self.platforms[cls.details.platform_id] = cls()
92+
return cls
9193

9294

9395
parser_manager = ParserManager()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from app.translator.platforms.arcsight.renders.arcsight_cti import ArcsightKeyword
1+
from app.translator.platforms.arcsight.renders.arcsight_cti import ArcsightKeyword # noqa: F401
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
ARCSIGHT_QUERY_DETAILS = {
2+
"platform_id": "arcsight",
3+
"name": "ArcSight Query",
4+
"group_name": "ArcSight",
5+
"group_id": "arcsight",
6+
"platform_name": "Query",
7+
"alt_platform_name": "CEF",
8+
}

uncoder-core/app/translator/platforms/arcsight/mappings/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)