Skip to content

Commit de73540

Browse files
committed
Various correlation query improvements
* Added possibility to pass arbitrary parameters to convert_correlation_search that are passed to the string format context. * Correlation multi-rule search query and typing query postprocessing. * Fix: missing aliasing in backend correlation support only raises errors if aliases appear in rule.
1 parent 8fdbffc commit de73540

File tree

3 files changed

+49
-14
lines changed

3 files changed

+49
-14
lines changed

sigma/conversion/base.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,6 +1613,7 @@ def convert_correlation_temporal_ordered_rule(
16131613
def convert_correlation_search(
16141614
self,
16151615
rule: SigmaCorrelationRule,
1616+
**kwargs,
16161617
) -> str:
16171618
if ( # if the correlation rule refers only a single rule and this rule results only in a single query
16181619
len(rule.rules) == 1
@@ -1625,6 +1626,7 @@ def convert_correlation_search(
16251626
normalization=self.convert_correlation_search_field_normalization_expression(
16261627
rule.aliases, rule_reference
16271628
),
1629+
**kwargs,
16281630
)
16291631
else:
16301632
return self.correlation_search_multi_rule_expression.format(
@@ -1633,7 +1635,9 @@ def convert_correlation_search(
16331635
self.correlation_search_multi_rule_query_expression.format(
16341636
rule=rule_reference.rule,
16351637
ruleid=rule_reference.rule.name or rule_reference.rule.id,
1636-
query=query,
1638+
query=self.convert_correlation_search_multi_rule_query_postprocess(
1639+
query
1640+
),
16371641
normalization=self.convert_correlation_search_field_normalization_expression(
16381642
rule.aliases,
16391643
rule_reference,
@@ -1642,33 +1646,43 @@ def convert_correlation_search(
16421646
for rule_reference in rule.rules
16431647
for query in rule_reference.rule.get_conversion_result()
16441648
)
1645-
)
1649+
),
1650+
**kwargs,
16461651
)
16471652

1653+
def convert_correlation_search_multi_rule_query_postprocess(
1654+
self,
1655+
query: str,
1656+
) -> str:
1657+
"""This function is called for each query in the multi-rule correlation search phase. It can be used to postprocess the query before it is joined with the other queries."""
1658+
return query
1659+
16481660
def convert_correlation_search_field_normalization_expression(
16491661
self,
16501662
aliases: SigmaCorrelationFieldAliases,
16511663
rule_reference: SigmaRule,
16521664
) -> str:
1653-
if (
1665+
if len(aliases) == 0:
1666+
return ""
1667+
elif (
16541668
self.correlation_search_field_normalization_expression is None
16551669
or self.correlation_search_field_normalization_expression_joiner is None
16561670
):
16571671
raise NotImplementedError(
16581672
"Correlation field normalization is not supported by backend."
16591673
)
1660-
1661-
return self.correlation_search_field_normalization_expression_joiner.join(
1662-
(
1663-
self.correlation_search_field_normalization_expression.format(
1664-
alias=alias.alias,
1665-
field=field,
1674+
else:
1675+
return self.correlation_search_field_normalization_expression_joiner.join(
1676+
(
1677+
self.correlation_search_field_normalization_expression.format(
1678+
alias=alias.alias,
1679+
field=field,
1680+
)
1681+
for alias in aliases
1682+
for alias_rule_reference, field in alias.mapping.items()
1683+
if alias_rule_reference == rule_reference
16661684
)
1667-
for alias in aliases
1668-
for alias_rule_reference, field in alias.mapping.items()
1669-
if alias_rule_reference == rule_reference
16701685
)
1671-
)
16721686

16731687
# Implementation of the typing phase of the correlation query.
16741688
def convert_correlation_typing(self, rule: SigmaCorrelationRule) -> str:
@@ -1681,14 +1695,21 @@ def convert_correlation_typing(self, rule: SigmaCorrelationRule) -> str:
16811695
self.typing_rule_query_expression.format(
16821696
rule=rule_reference.rule,
16831697
ruleid=rule_reference.rule.name or rule_reference.rule.id,
1684-
query=query,
1698+
query=self.convert_correlation_typing_query_postprocess(query),
16851699
)
16861700
for rule_reference in rule.rules
16871701
for query in rule_reference.rule.get_conversion_result()
16881702
)
16891703
)
16901704
)
16911705

1706+
def convert_correlation_typing_query_postprocess(
1707+
self,
1708+
query: str,
1709+
) -> str:
1710+
"""This function is called for each query in the typing phase of the correlation query. It can be used to postprocess the query before it is joined with the other queries."""
1711+
return query
1712+
16921713
# Implementation of the aggregation phase of the correlation query.
16931714
def convert_correlation_aggregation_from_template(
16941715
self, rule: SigmaCorrelationRule, correlation_type: SigmaCorrelationTypeLiteral, method: str

sigma/correlations.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ class SigmaCorrelationFieldAliases:
172172
def __iter__(self):
173173
return iter(self.aliases.values())
174174

175+
def __len__(self):
176+
return len(self.aliases)
177+
175178
@classmethod
176179
def from_dict(cls, d: dict):
177180
aliases = {}

tests/test_conversion_correlations.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ def test_event_count_correlation_single_rule_with_grouping(
4646
]
4747

4848

49+
def test_correlation_without_normalization_support(
50+
monkeypatch, test_backend, event_count_correlation_rule
51+
):
52+
monkeypatch.setattr(test_backend, "correlation_search_field_normalization_expression", None)
53+
assert test_backend.convert(event_count_correlation_rule) == [
54+
"""EventID=4625
55+
| aggregate window=5min count() as event_count by TargetUserName, TargetDomainName
56+
| where event_count >= 10"""
57+
]
58+
59+
4960
def test_generate_query_without_referenced_rules_expression(
5061
monkeypatch, test_backend, event_count_correlation_rule
5162
):

0 commit comments

Comments
 (0)