From 483a83ab1054bae4e3f46d45b3caab90507e60d2 Mon Sep 17 00:00:00 2001 From: Thomas Patzke Date: Sun, 8 Sep 2024 02:30:45 +0200 Subject: [PATCH] Option in ReplaceStringTransformation to skip special characters --- sigma/processing/transformations.py | 22 +++++++++++------ tests/test_processing_transformations.py | 30 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/sigma/processing/transformations.py b/sigma/processing/transformations.py index d6f2253c..d5581677 100644 --- a/sigma/processing/transformations.py +++ b/sigma/processing/transformations.py @@ -762,14 +762,17 @@ def apply( class ReplaceStringTransformation(StringValueTransformation): """ Replace string part matched by regular expresssion with replacement string that can reference - capture groups. It operates on the whole string representation of the SigmaString value. - Therefore, it is able to replace special characters like wildcards too. + capture groups. Normally, the replacement operates on the plain string representation of the + SigmaString. This allows also to include special characters and placeholders in the replacement. + By enabling the skip_special parameter, the replacement is only applied to the plain string + parts of a SigmaString and special characters and placeholders are left untouched. - This is basically an interface to re.sub() and can use all features available there. + The replacement is implemented with re.sub() and can use all features available there. """ regex: str replacement: str + skip_special: bool = False def __post_init__(self): super().__post_init__() @@ -782,10 +785,15 @@ def __post_init__(self): def apply_string_value(self, field: str, val: SigmaString) -> SigmaString: if isinstance(val, SigmaString): - sigma_string_plain = str(val) - replaced = self.re.sub(self.replacement, sigma_string_plain) - postprocessed_backslashes = re.sub(r"\\(?![*?])", r"\\\\", replaced) - return SigmaString(postprocessed_backslashes) + if self.skip_special: + return val.map_parts( + lambda s: self.re.sub(self.replacement, s), lambda p: isinstance(p, str) + ) + else: + sigma_string_plain = str(val) + replaced = self.re.sub(self.replacement, sigma_string_plain) + postprocessed_backslashes = re.sub(r"\\(?![*?])", r"\\\\", replaced) + return SigmaString(postprocessed_backslashes) @dataclass diff --git a/tests/test_processing_transformations.py b/tests/test_processing_transformations.py index c25e628e..1618d8bd 100644 --- a/tests/test_processing_transformations.py +++ b/tests/test_processing_transformations.py @@ -1356,6 +1356,36 @@ def test_replace_string_specials(dummy_pipeline): ) +def test_replace_string_skip_specials(dummy_pipeline): + sigma_rule = SigmaRule.from_dict( + { + "title": "Test", + "logsource": {"category": "test"}, + "detection": { + "test": [ + { + "field1": "*\\value", + "field2": 123, + } + ], + "condition": "test", + }, + } + ) + transformation = ReplaceStringTransformation("^.*\\\\", "/", True) + transformation.apply(dummy_pipeline, sigma_rule) + assert sigma_rule.detection.detections["test"] == SigmaDetection( + [ + SigmaDetection( + [ + SigmaDetectionItem("field1", [], [SigmaString("*/value")]), + SigmaDetectionItem("field2", [], [SigmaNumber(123)]), + ] + ) + ] + ) + + def test_replace_string_backslashes(dummy_pipeline): sigma_rule = SigmaRule.from_dict( {