Skip to content

Commit 0e5e0ca

Browse files
authored
Merge pull request #217 from UncoderIO/gis-9099
Gis 9099 add microsoft sentinel to one vendor flow
2 parents a749460 + e25af9b commit 0e5e0ca

File tree

5 files changed

+49
-12
lines changed

5 files changed

+49
-12
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def __init__(
7878
parsed_logsources: Optional[dict] = None,
7979
timeframe: Optional[timedelta] = None,
8080
query_period: Optional[timedelta] = None,
81-
mitre_attack: MitreInfoContainer = MitreInfoContainer(),
81+
mitre_attack: Optional[MitreInfoContainer] = None,
8282
raw_metainfo_container: Optional[RawMetaInfoContainer] = None,
8383
) -> None:
8484
self.id = id_ or str(uuid.uuid4())
@@ -88,7 +88,7 @@ def __init__(
8888
self.risk_score = risk_score
8989
self.type_ = type_ or ""
9090
self.description = description or ""
91-
self.author = [v.strip() for v in author] if author else []
91+
self.author = [v.strip() for v in author] if author and author != [None] else []
9292
self.date = date or datetime.now().date().strftime("%Y-%m-%d")
9393
self.output_table_fields = output_table_fields or []
9494
self.query_fields = query_fields or []
@@ -98,15 +98,15 @@ def __init__(
9898
self.severity = severity or SeverityType.low
9999
self.references = references or []
100100
self.tags = tags or []
101-
self.mitre_attack = mitre_attack or None
101+
self.mitre_attack = mitre_attack or MitreInfoContainer()
102102
self.raw_mitre_attack = raw_mitre_attack or []
103103
self.status = status or "stable"
104104
self.false_positives = false_positives or []
105105
self._source_mapping_ids = source_mapping_ids or [DEFAULT_MAPPING_NAME]
106106
self.parsed_logsources = parsed_logsources or {}
107107
self.timeframe = timeframe
108108
self.query_period = query_period
109-
self.raw_metainfo_container = raw_metainfo_container
109+
self.raw_metainfo_container = raw_metainfo_container or RawMetaInfoContainer()
110110

111111
@property
112112
def author_str(self) -> str:

uncoder-core/app/translator/platforms/microsoft/const.py

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

2020
PLATFORM_DETAILS = {"group_id": "sentinel", "group_name": "Microsoft Sentinel"}
2121

22+
_SENTINEL_KQL_QUERY = "sentinel-kql-query"
23+
_SENTINEL_KQL_RULE = "sentinel-kql-rule"
24+
2225
MICROSOFT_SENTINEL_QUERY_DETAILS = {
23-
"platform_id": "sentinel-kql-query",
26+
"platform_id": _SENTINEL_KQL_QUERY,
2427
"name": "Microsoft Sentinel Query",
2528
"platform_name": "Query (Kusto)",
2629
**PLATFORM_DETAILS,
2730
}
2831

2932
MICROSOFT_SENTINEL_RULE_DETAILS = {
30-
"platform_id": "sentinel-kql-rule",
33+
"platform_id": _SENTINEL_KQL_RULE,
3134
"name": "Microsoft Sentinel Rule",
3235
"platform_name": "Rule (Kusto)",
3336
"first_choice": 0,
@@ -50,6 +53,8 @@
5053
"group_id": "microsoft-defender",
5154
}
5255

56+
MICROSOFT_SENTINEL_QUERY_TYPES = {_SENTINEL_KQL_QUERY, _SENTINEL_KQL_RULE}
57+
5358
microsoft_defender_query_details = PlatformDetails(**MICROSOFT_DEFENDER_DETAILS)
5459
microsoft_sentinel_query_details = PlatformDetails(**MICROSOFT_SENTINEL_QUERY_DETAILS)
5560
microsoft_sentinel_rule_details = PlatformDetails(**MICROSOFT_SENTINEL_RULE_DETAILS)

uncoder-core/app/translator/platforms/microsoft/renders/microsoft_sentinel.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from app.translator.const import DEFAULT_VALUE_TYPE
2323
from app.translator.core.mapping import LogSourceSignature
2424
from app.translator.core.models.platform_details import PlatformDetails
25+
from app.translator.core.models.query_container import RawQueryContainer
2526
from app.translator.core.render import BaseFieldValueRender, PlatformQueryRender
2627
from app.translator.managers import render_manager
2728
from app.translator.platforms.microsoft.const import microsoft_sentinel_query_details
@@ -144,3 +145,6 @@ def generate_prefix(self, log_source_signature: LogSourceSignature, functions_pr
144145
@staticmethod
145146
def _finalize_search_query(query: str) -> str:
146147
return f"| where {query}" if query else ""
148+
149+
def generate_from_raw_query_container(self, query_container: RawQueryContainer) -> str:
150+
return query_container.query

uncoder-core/app/translator/platforms/microsoft/renders/microsoft_sentinel_rule.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from app.translator.core.custom_types.meta_info import SeverityType
2727
from app.translator.core.mapping import SourceMapping
2828
from app.translator.core.models.platform_details import PlatformDetails
29-
from app.translator.core.models.query_container import MetaInfoContainer, MitreInfoContainer
29+
from app.translator.core.models.query_container import MetaInfoContainer, MitreInfoContainer, RawQueryContainer
3030
from app.translator.managers import render_manager
3131
from app.translator.platforms.microsoft.const import DEFAULT_MICROSOFT_SENTINEL_RULE, microsoft_sentinel_rule_details
3232
from app.translator.platforms.microsoft.mapping import MicrosoftSentinelMappings, microsoft_sentinel_rule_mappings
@@ -107,7 +107,8 @@ def finalize_query(
107107
*args, # noqa: ARG002
108108
**kwargs, # noqa: ARG002
109109
) -> str:
110-
query = super().finalize_query(prefix=prefix, query=query, functions=functions)
110+
if not kwargs.get("raw_query", False):
111+
query = super().finalize_query(prefix=prefix, query=query, functions=functions)
111112
rule = copy.deepcopy(DEFAULT_MICROSOFT_SENTINEL_RULE)
112113
rule["query"] = query
113114
rule["displayName"] = meta_info.title or _AUTOGENERATED_TEMPLATE
@@ -130,3 +131,8 @@ def finalize_query(
130131
json_rule = json.dumps(rule, indent=4, sort_keys=False)
131132
json_rule = self.wrap_with_unmapped_fields(json_rule, unmapped_fields)
132133
return self.wrap_with_not_supported_functions(json_rule, not_supported_functions)
134+
135+
def generate_from_raw_query_container(self, query_container: RawQueryContainer) -> str:
136+
return self.finalize_query(
137+
prefix="", query=query_container.query, functions="", meta_info=query_container.meta_info, raw_query=True
138+
)

uncoder-core/app/translator/translator.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import logging
2-
from typing import Optional
2+
from collections import Counter
3+
from typing import Optional, Union
34

45
from app.translator.core.exceptions.core import UnsupportedPlatform
56
from app.translator.core.models.query_container import RawQueryContainer, TokenizedQueryContainer
6-
from app.translator.core.parser import QueryParser
7+
from app.translator.core.parser import PlatformQueryParser, QueryParser
78
from app.translator.core.render import QueryRender
89
from app.translator.managers import ParserManager, RenderManager, parser_manager, render_manager
910
from app.translator.platforms.elasticsearch.const import ELASTIC_QUERY_TYPES
11+
from app.translator.platforms.microsoft.const import MICROSOFT_SENTINEL_QUERY_TYPES
12+
from app.translator.platforms.roota.parsers.roota import RootAParser
13+
from app.translator.platforms.sigma.mapping import sigma_rule_mappings
1014
from app.translator.tools.decorators import handle_translation_exceptions
1115

1216

@@ -32,18 +36,36 @@ def __get_render(self, target: str) -> QueryRender:
3236

3337
@staticmethod
3438
def __is_one_vendor_translation(source: str, target: str) -> bool:
35-
vendors_query_types = [ELASTIC_QUERY_TYPES]
39+
vendors_query_types = [ELASTIC_QUERY_TYPES, MICROSOFT_SENTINEL_QUERY_TYPES]
3640
for vendor_query_types in vendors_query_types:
3741
if source in vendor_query_types and target in vendor_query_types:
3842
return True
3943

4044
return False
4145

42-
def parse_raw_query(self, text: str, source: str) -> tuple[QueryParser, RawQueryContainer]:
46+
def parse_raw_query(
47+
self, text: str, source: str
48+
) -> tuple[Union[PlatformQueryParser, RootAParser], RawQueryContainer]:
4349
parser = self.__get_parser(source)
4450
text = parser.remove_comments(text)
4551
return parser, parser.parse_raw_query(text, language=source)
4652

53+
def parse_meta_info(self, text: str, source: str) -> Union[dict, RawQueryContainer]:
54+
parser, raw_query_container = self.parse_raw_query(text=text, source=source)
55+
source_mappings = parser.get_source_mapping_ids_by_logsources(raw_query_container.query)
56+
log_sources = {"product": Counter(), "service": Counter(), "category": Counter()}
57+
sigma_source_mappings = sigma_rule_mappings.get_source_mappings_by_ids(
58+
[source_mapping.source_id for source_mapping in source_mappings], return_default=False
59+
)
60+
for sigma_source_mapping in sigma_source_mappings:
61+
if product := sigma_source_mapping.log_source_signature.log_sources.get("product"):
62+
log_sources["product"][product] += 1
63+
if service := sigma_source_mapping.log_source_signature.log_sources.get("service"):
64+
log_sources["service"][service] += 1
65+
if category := sigma_source_mapping.log_source_signature.log_sources.get("category"):
66+
log_sources["category"][category] += 1
67+
return log_sources, raw_query_container
68+
4769
@handle_translation_exceptions
4870
def __parse_incoming_data(
4971
self, text: str, source: str, target: Optional[str] = None

0 commit comments

Comments
 (0)