Skip to content

Commit

Permalink
Removed pipeline from processing transformations and conditions.
Browse files Browse the repository at this point in the history
Instead, processing pipelines can be set once with the newly introduced set_pipeline method.
  • Loading branch information
thomaspatzke committed Nov 10, 2024
1 parent c692b4c commit 9cb0a2c
Show file tree
Hide file tree
Showing 21 changed files with 597 additions and 588 deletions.
6 changes: 6 additions & 0 deletions sigma/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ class SigmaPipelineParsingError(SigmaError):
pass


class SigmaProcessingItemError(SigmaError):
"""Error in definition or state of processing item."""

pass


class SigmaPlaceholderError(SigmaValueError):
"""Attempted to convert an unhandled Placeholder into a query"""

Expand Down
57 changes: 30 additions & 27 deletions sigma/processing/conditions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,36 @@
import sigma
from sigma.correlations import SigmaCorrelationRule
from sigma.types import SigmaFieldReference, SigmaType
from typing import Literal, Union
from typing import Literal, Optional, Union
from sigma.rule import (
SigmaDetection,
SigmaRule,
SigmaDetectionItem,
)
from sigma.exceptions import SigmaConfigurationError, SigmaRegularExpressionError
from sigma.exceptions import (
SigmaConfigurationError,
SigmaProcessingItemError,
SigmaRegularExpressionError,
)


@dataclass
class ProcessingCondition(ABC):
"""Anchor base class for all processing condition types."""

_pipeline: Optional["sigma.processing.pipeline.ProcessingPipeline"] = field(
init=False, compare=False, default=None
)

def set_pipeline(self, pipeline: "sigma.processing.pipeline.ProcessingPipeline"):
if self._pipeline is None:
self._pipeline = pipeline
else:
raise SigmaProcessingItemError(f"Pipeline for condition was already set.")

def _clear_pipeline(self) -> None:
self._pipeline = None


@dataclass
class RuleProcessingCondition(ProcessingCondition, ABC):
Expand All @@ -26,7 +44,6 @@ class RuleProcessingCondition(ProcessingCondition, ABC):
@abstractmethod
def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
"""Match condition on Sigma rule."""
Expand All @@ -39,14 +56,11 @@ class FieldNameProcessingCondition(ProcessingCondition, ABC):
"""

@abstractmethod
def match_field_name(
self, pipeline: "sigma.processing.pipeline.ProcessingPipeline", field: str
) -> bool:
def match_field_name(self, field: str) -> bool:
"The method match is called for each field name and must return a bool result."

def match_detection_item(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
) -> bool:
"""
Expand All @@ -60,37 +74,33 @@ def match_detection_item(
* `match_detection_item_value` for the whole value list of a detection item and
* `match_value` for single detection items values.
"""
return self.match_detection_item_field(
pipeline, detection_item
) or self.match_detection_item_value(pipeline, detection_item)
return self.match_detection_item_field(detection_item) or self.match_detection_item_value(
detection_item
)

def match_detection_item_field(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
) -> bool:
"""Returns True if the field of the detection item matches the implemented field name condition."""
return self.match_field_name(pipeline, detection_item.field)
return self.match_field_name(detection_item.field)

def match_detection_item_value(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
) -> bool:
"""Returns True if any value of a detection item contains a field reference to a field name
matching the implemented field name condition. Processing actions must only be applied to
matching individual values determined by `match_value`."""
return any((self.match_value(pipeline, value) for value in detection_item.value))
return any((self.match_value(value) for value in detection_item.value))

def match_value(
self, pipeline: "sigma.processing.pipeline.ProcessingPipeline", value: SigmaType
) -> bool:
def match_value(self, value: SigmaType) -> bool:
"""
Checks if a detection item value matches the field name condition implemented in
`match_field_name` if it is a field reference. For all other types the method returns False.
"""
if isinstance(value, SigmaFieldReference):
return self.match_field_name(pipeline, value.field)
return self.match_field_name(value.field)
else:
return False

Expand All @@ -104,7 +114,6 @@ class DetectionItemProcessingCondition(ProcessingCondition, ABC):
@abstractmethod
def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
) -> bool:
"""Match condition on Sigma rule."""
Expand Down Expand Up @@ -134,17 +143,12 @@ def __post_init__(self):

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
) -> bool:
return self.match_func(
(self.match_value(pipeline, value) for value in detection_item.value)
)
return self.match_func((self.match_value(value) for value in detection_item.value))

@abstractmethod
def match_value(
self, pipeline: "sigma.processing.pipeline.ProcessingPipeline", value: SigmaType
) -> bool:
def match_value(self, value: SigmaType) -> bool:
"""Match condition on detection item values."""


Expand All @@ -154,7 +158,6 @@ class RuleDetectionItemCondition(RuleProcessingCondition, ABC):

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
if isinstance(rule, SigmaRule):
Expand Down
4 changes: 1 addition & 3 deletions sigma/processing/conditions/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def __post_init__(self):

def match_field_name(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
field: Optional[str],
) -> bool:
if field is None:
Expand All @@ -59,7 +58,6 @@ class ExcludeFieldCondition(IncludeFieldCondition):

def match_field_name(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
) -> bool:
return not super().match_field_name(pipeline, detection_item)
return not super().match_field_name(detection_item)
7 changes: 1 addition & 6 deletions sigma/processing/conditions/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ def __post_init__(self):

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
if isinstance(rule, SigmaRule):
Expand All @@ -47,7 +46,7 @@ def match(
# Will only return true if the rules have been resolved in advance
for ref in rule.rules:
if hasattr(ref, "rule") and isinstance(ref.rule, (SigmaRule, SigmaCorrelationRule)):
if self.match(pipeline, ref.rule):
if self.match(ref.rule):
return True
return False

Expand Down Expand Up @@ -109,7 +108,6 @@ class IsSigmaRuleCondition(RuleProcessingCondition):

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
return isinstance(rule, SigmaRule)
Expand All @@ -123,7 +121,6 @@ class IsSigmaCorrelationRuleCondition(RuleProcessingCondition):

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
return isinstance(rule, SigmaCorrelationRule)
Expand Down Expand Up @@ -166,7 +163,6 @@ def __post_init__(self):

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
try: # first try to get built-in attribute
Expand Down Expand Up @@ -239,7 +235,6 @@ def __post_init__(self):

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
return self.match_tag in rule.tags
28 changes: 12 additions & 16 deletions sigma/processing/conditions/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
SigmaRule,
SigmaDetectionItem,
)
from sigma.exceptions import SigmaConfigurationError
from sigma.exceptions import SigmaConfigurationError, SigmaProcessingItemError


@dataclass
Expand All @@ -25,7 +25,6 @@ class RuleProcessingItemAppliedCondition(RuleProcessingCondition):

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
return rule.was_processed_by(self.processing_item_id)
Expand All @@ -42,11 +41,15 @@ class ProcessingStateConditionBase:
val: Union[str, int, float, bool]
op: Literal["eq", "ne", "gte", "gt", "lte", "lt"] = field(default="eq")

def match_state(self, pipeline: "sigma.processing.pipeline.ProcessingPipeline") -> bool:
def match_state(self) -> bool:
try:
state_val = pipeline.state[self.key]
state_val = self._pipeline.state[self.key]
except KeyError:
return False
except AttributeError:
raise SigmaProcessingItemError(
"No processing pipeline was passed to condition, but required by it"
)

if self.op == "eq":
return state_val == self.val
Expand Down Expand Up @@ -74,10 +77,9 @@ class RuleProcessingStateCondition(RuleProcessingCondition, ProcessingStateCondi

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
rule: Union[SigmaRule, SigmaCorrelationRule],
) -> bool:
return self.match_state(pipeline)
return self.match_state()


@dataclass
Expand All @@ -88,10 +90,9 @@ class FieldNameProcessingStateCondition(FieldNameProcessingCondition, Processing

def match_field_name(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
field: str,
) -> bool:
return self.match_state(pipeline)
return self.match_state()


@dataclass
Expand All @@ -104,7 +105,6 @@ class DetectionItemProcessingItemAppliedCondition(DetectionItemProcessingConditi

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
) -> bool:
return detection_item.was_processed_by(self.processing_item_id)
Expand All @@ -120,10 +120,9 @@ class DetectionItemProcessingStateCondition(

def match(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
) -> bool:
return self.match_state(pipeline)
return self.match_state()


@dataclass
Expand All @@ -134,14 +133,11 @@ class FieldNameProcessingItemAppliedCondition(FieldNameProcessingCondition):

processing_item_id: str

def match_field_name(
self, pipeline: "sigma.processing.pipeline.ProcessingPipeline", field: str
) -> bool:
return pipeline.field_was_processed_by(field, self.processing_item_id)
def match_field_name(self, field: str) -> bool:
return self._pipeline.field_was_processed_by(field, self.processing_item_id)

def match_detection_item(
self,
pipeline: "sigma.processing.pipeline.ProcessingPipeline",
detection_item: SigmaDetectionItem,
):
return detection_item.was_processed_by(self.processing_item_id)
12 changes: 3 additions & 9 deletions sigma/processing/conditions/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ def __post_init__(self):
f"Regular expression '{self.pattern}' is invalid: {str(e)}"
) from e

def match_value(
self, pipeline: "sigma.processing.pipeline.ProcessingPipeline", value: SigmaType
) -> bool:
def match_value(self, value: SigmaType) -> bool:
if isinstance(value, SigmaString):
result = self.re.match(str(value))
else:
Expand All @@ -48,9 +46,7 @@ class ContainsWildcardCondition(ValueProcessingCondition):
Evaluates to True if the value contains a wildcard character.
"""

def match_value(
self, pipeline: "sigma.processing.pipeline.ProcessingPipeline", value: SigmaType
) -> bool:
def match_value(self, value: SigmaType) -> bool:
if isinstance(value, SigmaString):
return value.contains_special()
else:
Expand All @@ -65,7 +61,5 @@ class IsNullCondition(ValueProcessingCondition):
false result in all match mode.
"""

def match_value(
self, pipeline: "sigma.processing.pipeline.ProcessingPipeline", value: SigmaType
) -> bool:
def match_value(self, value: SigmaType) -> bool:
return isinstance(value, SigmaNull)
Loading

0 comments on commit 9cb0a2c

Please sign in to comment.