Skip to content

Commit 32a1d12

Browse files
authored
Merge pull request #288 from SigmaHQ:issue-cs-17
Introduced interpret_special option to ReplaceStringTransformation
2 parents 8738362 + 001bbbd commit 32a1d12

File tree

4 files changed

+64
-10
lines changed

4 files changed

+64
-10
lines changed

sigma/processing/transformations.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -765,14 +765,17 @@ class ReplaceStringTransformation(StringValueTransformation):
765765
capture groups. Normally, the replacement operates on the plain string representation of the
766766
SigmaString. This allows also to include special characters and placeholders in the replacement.
767767
By enabling the skip_special parameter, the replacement is only applied to the plain string
768-
parts of a SigmaString and special characters and placeholders are left untouched.
768+
parts of a SigmaString and special characters and placeholders are left untouched. The
769+
interpret_special option determines for skip_special if special characters and placeholders are
770+
interpreted in the replacement result or not.
769771
770772
The replacement is implemented with re.sub() and can use all features available there.
771773
"""
772774

773775
regex: str
774776
replacement: str
775777
skip_special: bool = False
778+
interpret_special: bool = False
776779

777780
def __post_init__(self):
778781
super().__post_init__()
@@ -787,7 +790,9 @@ def apply_string_value(self, field: str, val: SigmaString) -> SigmaString:
787790
if isinstance(val, SigmaString):
788791
if self.skip_special:
789792
return val.map_parts(
790-
lambda s: self.re.sub(self.replacement, s), lambda p: isinstance(p, str)
793+
lambda s: self.re.sub(self.replacement, s),
794+
lambda p: isinstance(p, str),
795+
self.interpret_special,
791796
)
792797
else:
793798
sigma_string_plain = str(val)

sigma/types.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -501,14 +501,21 @@ def map_parts(
501501
self,
502502
func: Callable[[Union[str, SpecialChars]], Optional[Union[str, SpecialChars]]],
503503
filter_func: Callable[[Union[str, SpecialChars]], bool] = lambda x: True,
504+
interpret_special: bool = False,
504505
) -> "SigmaString":
505506
s = self.__class__()
506-
s.s = tuple(
507-
filter(
508-
lambda x: x is not None, # filter out None results
509-
(func(item) if filter_func(item) else item for item in self.iter_parts()),
510-
)
511-
)
507+
parts = []
508+
for item in self.iter_parts():
509+
if filter_func(item):
510+
result = func(item)
511+
if result is not None:
512+
if interpret_special:
513+
parts.extend(SigmaString(result).s)
514+
else:
515+
parts.append(result)
516+
else:
517+
parts.append(item)
518+
s.s = tuple(parts)
512519
return s
513520

514521
def convert(

tests/test_processing_transformations.py

+32-2
Original file line numberDiff line numberDiff line change
@@ -1416,13 +1416,43 @@ def test_replace_string_skip_specials(dummy_pipeline):
14161416
},
14171417
}
14181418
)
1419-
transformation = ReplaceStringTransformation("^.*\\\\", "/", True)
1419+
transformation = ReplaceStringTransformation("^.*\\\\", "/?/", True)
14201420
transformation.apply(dummy_pipeline, sigma_rule)
14211421
assert sigma_rule.detection.detections["test"] == SigmaDetection(
14221422
[
14231423
SigmaDetection(
14241424
[
1425-
SigmaDetectionItem("field1", [], [SigmaString("*/value")]),
1425+
SigmaDetectionItem("field1", [], [SigmaString("*/\\?/value")]),
1426+
SigmaDetectionItem("field2", [], [SigmaNumber(123)]),
1427+
]
1428+
)
1429+
]
1430+
)
1431+
1432+
1433+
def test_replace_string_skip_specials_with_interpret_specials(dummy_pipeline):
1434+
sigma_rule = SigmaRule.from_dict(
1435+
{
1436+
"title": "Test",
1437+
"logsource": {"category": "test"},
1438+
"detection": {
1439+
"test": [
1440+
{
1441+
"field1": "*\\value",
1442+
"field2": 123,
1443+
}
1444+
],
1445+
"condition": "test",
1446+
},
1447+
}
1448+
)
1449+
transformation = ReplaceStringTransformation("^.*\\\\", "/?/", True, True)
1450+
transformation.apply(dummy_pipeline, sigma_rule)
1451+
assert sigma_rule.detection.detections["test"] == SigmaDetection(
1452+
[
1453+
SigmaDetection(
1454+
[
1455+
SigmaDetectionItem("field1", [], [SigmaString("*/?/value")]),
14261456
SigmaDetectionItem("field2", [], [SigmaNumber(123)]),
14271457
]
14281458
)

tests/test_types.py

+12
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,18 @@ def test_string_map_parts(sigma_string):
491491
)
492492

493493

494+
def test_string_map_parts_interpret_special(sigma_string):
495+
assert sigma_string.map_parts(lambda x: x.upper(), lambda x: isinstance(x, str), True).s == (
496+
SpecialChars.WILDCARD_MULTI,
497+
"TEST",
498+
SpecialChars.WILDCARD_MULTI,
499+
"STR",
500+
SpecialChars.WILDCARD_MULTI,
501+
"ING",
502+
SpecialChars.WILDCARD_MULTI,
503+
)
504+
505+
494506
def test_cased_string(sigma_string):
495507
assert SigmaCasedString.from_sigma_string(sigma_string) == SigmaCasedString("*Test*Str\\*ing*")
496508

0 commit comments

Comments
 (0)