Skip to content

Commit d36eb0e

Browse files
committed
fixing oring regex architecture and adding fields cleaning in it to support nested fields
1 parent 5b47835 commit d36eb0e

File tree

2 files changed

+69
-23
lines changed

2 files changed

+69
-23
lines changed

sigma/backends/splunk/splunk.py

+31-23
Original file line numberDiff line numberDiff line change
@@ -41,33 +41,45 @@ class SplunkDeferredORRegularExpression(DeferredTextQueryExpression):
4141
}
4242

4343
def __init__(self, state, field, arg) -> None:
44-
SplunkDeferredORRegularExpression.add_field(field)
45-
index_suffix = SplunkDeferredORRegularExpression.get_index_suffix(field)
46-
self.template = (
47-
'rex field={field} "(?<{field}Match'
48-
+ index_suffix
49-
+ '>{value})"\n| eval {field}Condition'
50-
+ index_suffix
51-
+ "=if(isnotnull({field}Match"
52-
+ index_suffix
53-
+ '), "true", "false")'
44+
self.add_field(field)
45+
field_condition = self.get_field_condition(field)
46+
field_match = self.get_field_match(field)
47+
self.template = 'rex field={{field}} "(?<{field_match}>{{value}})"\n| eval {field_condition}=if(isnotnull({field_match}), "true", "false")'.format(
48+
field_match=field_match, field_condition=field_condition
5449
)
5550
return super().__init__(state, field, arg)
5651

52+
@staticmethod
53+
def clean_field(field):
54+
# splunk does not allow dots in regex group, so we need to clean variables
55+
return re.sub(".*\\.", "", field)
56+
5757
@classmethod
5858
def add_field(cls, field):
5959
cls.field_counts[field] = (
6060
cls.field_counts.get(field, 0) + 1
6161
) # increment the field count
6262

6363
@classmethod
64-
def get_index_suffix(cls, field):
65-
66-
index_suffix = cls.field_counts.get(field, 0)
64+
def get_field_suffix(cls, field):
65+
index_suffix = cls.field_counts.get(field, "")
6766
if index_suffix == 1:
68-
# return nothing for the first field use
69-
return ""
70-
return str(index_suffix)
67+
index_suffix = ""
68+
return index_suffix
69+
70+
@classmethod
71+
def construct_field_variable(cls, field, variable):
72+
cleaned_field = cls.clean_field(field)
73+
index_suffix = cls.get_field_suffix(field)
74+
return f"{cleaned_field}{variable}{index_suffix}"
75+
76+
@classmethod
77+
def get_field_match(cls, field):
78+
return cls.construct_field_variable(field, "Match")
79+
80+
@classmethod
81+
def get_field_condition(cls, field):
82+
return cls.construct_field_variable(field, "Condition")
7183

7284
@classmethod
7385
def reset(cls):
@@ -248,9 +260,7 @@ def convert_condition_field_eq_val_re(
248260
).postprocess(None, cond)
249261

250262
cond_true = ConditionFieldEqualsValueExpression(
251-
cond.field
252-
+ "Condition"
253-
+ str(SplunkDeferredORRegularExpression.get_index_suffix(cond.field)),
263+
SplunkDeferredORRegularExpression.get_field_condition(cond.field),
254264
SigmaString("true"),
255265
)
256266
# returning fieldX=true
@@ -381,13 +391,11 @@ def finalize_query_data_model(
381391
cim_fields = " ".join(
382392
splunk_sysmon_process_creation_cim_mapping.values()
383393
)
384-
394+
385395
elif rule.logsource.category == "proxy":
386396
data_model = "Web"
387397
data_set = "Proxy"
388-
cim_fields = " ".join(
389-
splunk_web_proxy_cim_mapping.values()
390-
)
398+
cim_fields = " ".join(splunk_web_proxy_cim_mapping.values())
391399

392400
try:
393401
data_model_set = state.processing_state["data_model_set"]

tests/test_backend_splunk.py

+38
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import pytest
33
from sigma.backends.splunk import SplunkBackend
44
from sigma.collection import SigmaCollection
5+
from sigma.processing.pipeline import ProcessingPipeline
56
from sigma.pipelines.splunk import splunk_cim_data_model
67

78

@@ -224,6 +225,43 @@ def test_splunk_regex_query_explicit_or(splunk_backend: SplunkBackend):
224225
)
225226

226227

228+
def test_splunk_regex_query_explicit_or_with_nested_fields():
229+
230+
pipeline = ProcessingPipeline.from_yaml(
231+
"""
232+
name: Test
233+
priority: 100
234+
transformations:
235+
- id: field_mapping
236+
type: field_name_mapping
237+
mapping:
238+
fieldA: Event.EventData.fieldA
239+
fieldB: Event.EventData.fieldB
240+
"""
241+
)
242+
splunk_backend = SplunkBackend(pipeline)
243+
244+
collection = SigmaCollection.from_yaml(
245+
"""
246+
title: Test
247+
status: test
248+
logsource:
249+
category: test_category
250+
product: test_product
251+
detection:
252+
sel1:
253+
fieldA|re: foo.*bar
254+
sel2:
255+
fieldB|re: boo.*foo
256+
condition: sel1 or sel2
257+
"""
258+
)
259+
260+
assert splunk_backend.convert(collection) == [
261+
'\n| rex field=Event.EventData.fieldA "(?<fieldAMatch>foo.*bar)"\n| eval fieldACondition=if(isnotnull(fieldAMatch), "true", "false")\n| rex field=Event.EventData.fieldB "(?<fieldBMatch>boo.*foo)"\n| eval fieldBCondition=if(isnotnull(fieldBMatch), "true", "false")\n| search fieldACondition="true" OR fieldBCondition="true"'
262+
]
263+
264+
227265
def test_splunk_single_regex_query(splunk_backend: SplunkBackend):
228266
assert (
229267
splunk_backend.convert(

0 commit comments

Comments
 (0)