Skip to content

Commit 8d06469

Browse files
authored
Merge pull request #192 from UncoderIO/gis-8502
Gis 8502
2 parents 3f24987 + 9a8bdba commit 8d06469

File tree

6 files changed

+94
-5
lines changed

6 files changed

+94
-5
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,9 @@ class InvalidJSONStructure(InvalidRuleStructure):
8484
rule_type: str = "JSON"
8585

8686

87+
class InvalidTOMLStructure(InvalidRuleStructure):
88+
rule_type: str = "TOML"
89+
90+
8791
class InvalidXMLStructure(InvalidRuleStructure):
8892
rule_type: str = "XML"

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import json
22
from typing import Union
33

4+
import toml
45
import xmltodict
56
import yaml
67

7-
from app.translator.core.exceptions.core import InvalidJSONStructure, InvalidXMLStructure, InvalidYamlStructure
8+
from app.translator.core.exceptions.core import (
9+
InvalidJSONStructure,
10+
InvalidTOMLStructure,
11+
InvalidXMLStructure,
12+
InvalidYamlStructure,
13+
)
814
from app.translator.core.mitre import MitreConfig, MitreInfoContainer
915

1016

@@ -50,3 +56,14 @@ def load_rule(text: Union[str, bytes]) -> dict:
5056
return xmltodict.parse(text)
5157
except Exception as err:
5258
raise InvalidXMLStructure(error=str(err)) from err
59+
60+
61+
class TOMLRuleMixin:
62+
mitre_config: MitreConfig = MitreConfig()
63+
64+
@staticmethod
65+
def load_rule(text: str) -> dict:
66+
try:
67+
return toml.loads(text)
68+
except toml.TomlDecodeError as err:
69+
raise InvalidTOMLStructure(error=str(err)) from err

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,26 @@ def __init__(
3939
trigger_threshold: Optional[str] = None,
4040
query_frequency: Optional[str] = None,
4141
query_period: Optional[str] = None,
42+
from_: Optional[str] = None,
43+
interval: Optional[str] = None,
4244
) -> None:
4345
self.trigger_operator = trigger_operator
4446
self.trigger_threshold = trigger_threshold
4547
self.query_frequency = query_frequency
4648
self.query_period = query_period
49+
self.from_ = from_
50+
self.interval = interval
4751

4852

4953
class MetaInfoContainer:
5054
def __init__(
5155
self,
5256
*,
5357
id_: Optional[str] = None,
58+
index: Optional[list[str]] = None,
59+
language: Optional[str] = None,
60+
risk_score: Optional[int] = None,
61+
type_: Optional[str] = None,
5462
title: Optional[str] = None,
5563
description: Optional[str] = None,
5664
author: Optional[list[str]] = None,
@@ -73,6 +81,10 @@ def __init__(
7381
) -> None:
7482
self.id = id_ or str(uuid.uuid4())
7583
self.title = title or ""
84+
self.index = index or []
85+
self.language = language or ""
86+
self.risk_score = risk_score
87+
self.type_ = type_ or ""
7688
self.description = description or ""
7789
self.author = [v.strip() for v in author] if author else []
7890
self.date = date or datetime.now().date().strftime("%Y-%m-%d")

uncoder-core/app/translator/platforms/elasticsearch/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
from app.translator.platforms.elasticsearch.parsers.detection_rule import ElasticSearchRuleParser # noqa: F401
1+
from app.translator.platforms.elasticsearch.parsers.detection_rule import (
2+
ElasticSearchRuleParser, # noqa: F401
3+
ElasticSearchRuleTOMLParser, # noqa: F401
4+
)
25
from app.translator.platforms.elasticsearch.parsers.elasticsearch import ElasticSearchQueryParser # noqa: F401
36
from app.translator.platforms.elasticsearch.renders.detection_rule import ElasticSearchRuleRender # noqa: F401
47
from app.translator.platforms.elasticsearch.renders.elast_alert import ElastAlertRuleRender # noqa: F401

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
_ELASTIC_LUCENE_QUERY = "elastic-lucene-query"
77
_ELASTIC_LUCENE_RULE = "elastic-lucene-rule"
8+
_ELASTIC_LUCENE_RULE_TOML = "elastic-lucene-rule-toml"
89
_ELASTIC_KIBANA_RULE = "elastic-kibana-rule"
910
_ELASTALERT_LUCENE_RULE = "elastalert-lucene-rule"
1011
_ELASTIC_WATCHER_RULE = "elastic-watcher-rule"
@@ -50,6 +51,14 @@
5051
**PLATFORM_DETAILS,
5152
}
5253

54+
ELASTICSEARCH_RULE_TOML_DETAILS = {
55+
"platform_id": _ELASTIC_LUCENE_RULE_TOML,
56+
"name": "Elastic Rule TOML",
57+
"platform_name": "Detection Rule (Lucene) TOML",
58+
"first_choice": 0,
59+
**PLATFORM_DETAILS,
60+
}
61+
5362
KIBANA_DETAILS = {
5463
"platform_id": _ELASTIC_KIBANA_RULE,
5564
"name": "Elastic Kibana Saved Search",
@@ -78,6 +87,7 @@
7887
elasticsearch_esql_query_details = PlatformDetails(**ELASTICSEARCH_ESQL_QUERY_DETAILS)
7988
elasticsearch_esql_rule_details = PlatformDetails(**ELASTICSEARCH_ESQL_RULE_DETAILS)
8089
elasticsearch_rule_details = PlatformDetails(**ELASTICSEARCH_RULE_DETAILS)
90+
elasticsearch_rule_toml_details = PlatformDetails(**ELASTICSEARCH_RULE_TOML_DETAILS)
8191
elastalert_details = PlatformDetails(**ELASTALERT_DETAILS)
8292
kibana_rule_details = PlatformDetails(**KIBANA_DETAILS)
8393
xpack_watcher_details = PlatformDetails(**XPACK_WATCHER_DETAILS)

uncoder-core/app/translator/platforms/elasticsearch/parsers/detection_rule.py

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1616
-----------------------------------------------------------------
1717
"""
18+
from datetime import datetime
1819

19-
from app.translator.core.mixins.rule import JsonRuleMixin
20+
from app.translator.core.mixins.rule import JsonRuleMixin, TOMLRuleMixin
2021
from app.translator.core.models.platform_details import PlatformDetails
21-
from app.translator.core.models.query_container import MetaInfoContainer, RawQueryContainer
22+
from app.translator.core.models.query_container import MetaInfoContainer, RawMetaInfoContainer, RawQueryContainer
2223
from app.translator.managers import parser_manager
23-
from app.translator.platforms.elasticsearch.const import elasticsearch_rule_details
24+
from app.translator.platforms.elasticsearch.const import elasticsearch_rule_details, elasticsearch_rule_toml_details
2425
from app.translator.platforms.elasticsearch.parsers.elasticsearch import ElasticSearchQueryParser
2526
from app.translator.tools.utils import parse_rule_description_str
2627

@@ -53,3 +54,45 @@ def parse_raw_query(self, text: str, language: str) -> RawQueryContainer:
5354
mitre_attack=mitre_attack,
5455
),
5556
)
57+
58+
59+
@parser_manager.register
60+
class ElasticSearchRuleTOMLParser(ElasticSearchQueryParser, TOMLRuleMixin):
61+
details: PlatformDetails = elasticsearch_rule_toml_details
62+
63+
def parse_raw_query(self, text: str, language: str) -> RawQueryContainer:
64+
raw_rule = self.load_rule(text=text)
65+
rule = raw_rule.get("rule")
66+
metadata = raw_rule.get("metadata")
67+
techniques = []
68+
for threat_data in rule.get("threat", []):
69+
if threat_data.get("technique"):
70+
techniques.append(threat_data["technique"][0]["id"].lower())
71+
mitre_attack = self.mitre_config.get_mitre_info(
72+
tactics=[threat_data["tactic"]["name"].replace(" ", "_").lower() for threat_data in rule.get("threat", [])],
73+
techniques=techniques,
74+
)
75+
date = None
76+
if metadata.get("creation_date"):
77+
date = datetime.strptime(metadata.get("creation_date"), "%Y/%m/%d").strftime("%Y-%m-%d")
78+
return RawQueryContainer(
79+
query=rule["query"],
80+
language=language,
81+
meta_info=MetaInfoContainer(
82+
id_=rule.get("rule_id"),
83+
title=rule.get("name"),
84+
description=rule.get("description"),
85+
author=rule.get("author"),
86+
date=date,
87+
license_=rule.get("license"),
88+
severity=rule.get("severity"),
89+
references=rule.get("references"),
90+
tags=rule.get("tags"),
91+
mitre_attack=mitre_attack,
92+
index=rule.get("index"),
93+
language=rule.get("language"),
94+
risk_score=rule.get("risk_score"),
95+
type_=rule.get("type"),
96+
raw_metainfo_container=RawMetaInfoContainer(from_=rule.get("from"), interval=rule.get("interval")),
97+
),
98+
)

0 commit comments

Comments
 (0)