pySigma-backend-sqlite 1.1.1__tar.gz → 1.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pySigma-backend-sqlite
3
- Version: 1.1.1
3
+ Version: 1.1.2
4
4
  Summary: pySigma sqlite backend
5
5
  License-Expression: LGPL-3.0-only
6
6
  License-File: LICENSE
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Classifier: Programming Language :: Python :: 3.14
15
- Requires-Dist: pysigma (>=1.0.2,<=1.1.0)
15
+ Requires-Dist: pysigma (>=1.0.2,<=2.0)
16
16
  Project-URL: Repository, https://github.com/SigmaHQ/pySigma-backend-sqlite
17
17
  Description-Content-Type: text/markdown
18
18
 
@@ -126,17 +126,18 @@ from sigma.backends.sqlite import sqlite
126
126
  from sigma.pipelines.sysmon import sysmon_pipeline
127
127
  from sigma.pipelines.windows import windows_logsource_pipeline
128
128
 
129
- from sigma.processing.resolver import ProcessingPipelineResolver
130
-
131
- # Create the pipeline resolver
132
- piperesolver = ProcessingPipelineResolver()
133
- # Add pipelines
134
- piperesolver.add_pipeline_class(sysmon_pipeline()) # Syssmon
135
- piperesolver.add_pipeline_class(windows_logsource_pipeline()) # Windows
136
- # Create a combined pipeline
137
- combined_pipeline = piperesolver.resolve(piperesolver.pipelines)
138
- # Instantiate backend using the combined pipeline
129
+ # Combine pipelines to map both Channel and EventID:
130
+ # 1. sysmon_pipeline: maps category (e.g., process_creation) -> EventID (e.g., 1)
131
+ # and changes logsource to service=sysmon
132
+ # 2. windows_logsource_pipeline: maps service=sysmon -> Channel
133
+ #
134
+ # For process_creation/windows, this produces:
135
+ # Channel='Microsoft-Windows-Sysmon/Operational' AND EventID=1
136
+ combined_pipeline = sysmon_pipeline() + windows_logsource_pipeline()
139
137
  sqlite_backend = sqlite.sqliteBackend(combined_pipeline)
138
+ # Set the table name for the generated SQL queries
139
+ sqlite_backend.table = "logs"
140
+
140
141
 
141
142
  rule = SigmaCollection.from_yaml(
142
143
  r"""
@@ -108,17 +108,18 @@ from sigma.backends.sqlite import sqlite
108
108
  from sigma.pipelines.sysmon import sysmon_pipeline
109
109
  from sigma.pipelines.windows import windows_logsource_pipeline
110
110
 
111
- from sigma.processing.resolver import ProcessingPipelineResolver
112
-
113
- # Create the pipeline resolver
114
- piperesolver = ProcessingPipelineResolver()
115
- # Add pipelines
116
- piperesolver.add_pipeline_class(sysmon_pipeline()) # Syssmon
117
- piperesolver.add_pipeline_class(windows_logsource_pipeline()) # Windows
118
- # Create a combined pipeline
119
- combined_pipeline = piperesolver.resolve(piperesolver.pipelines)
120
- # Instantiate backend using the combined pipeline
111
+ # Combine pipelines to map both Channel and EventID:
112
+ # 1. sysmon_pipeline: maps category (e.g., process_creation) -> EventID (e.g., 1)
113
+ # and changes logsource to service=sysmon
114
+ # 2. windows_logsource_pipeline: maps service=sysmon -> Channel
115
+ #
116
+ # For process_creation/windows, this produces:
117
+ # Channel='Microsoft-Windows-Sysmon/Operational' AND EventID=1
118
+ combined_pipeline = sysmon_pipeline() + windows_logsource_pipeline()
121
119
  sqlite_backend = sqlite.sqliteBackend(combined_pipeline)
120
+ # Set the table name for the generated SQL queries
121
+ sqlite_backend.table = "logs"
122
+
122
123
 
123
124
  rule = SigmaCollection.from_yaml(
124
125
  r"""
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pySigma-backend-sqlite"
3
- version = "1.1.1"
3
+ version = "1.1.2"
4
4
  description = "pySigma sqlite backend"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -15,7 +15,7 @@ packages = [
15
15
 
16
16
  [tool.poetry.dependencies]
17
17
  python = "^3.10"
18
- pysigma = ">=1.0.2,<=1.1.0"
18
+ pysigma = ">=1.0.2,<=2.0"
19
19
 
20
20
  [tool.poetry.group.dev.dependencies]
21
21
  black = "^24.1"
@@ -623,14 +623,69 @@ class sqliteBackend(TextQueryBackend):
623
623
 
624
624
  return sqlite_query
625
625
 
626
+ def _extract_field_values_from_rule(
627
+ self, rule: SigmaRule, field_name: str
628
+ ) -> List[Any]:
629
+ """
630
+ Extract all values for a given field name from a rule's detection section.
631
+ Returns a list of unique values found for the specified field.
632
+ """
633
+ from sigma.rule import SigmaDetectionItem
634
+ from sigma.types import SigmaString, SigmaNumber
635
+
636
+ values = set()
637
+ field_name_lower = field_name.lower()
638
+
639
+ # Iterate through all detection groups (sel, filter, etc.)
640
+ for detection_name, detection in rule.detection.detections.items():
641
+ # Each detection has detection_items
642
+ self._extract_values_from_detection(detection, field_name_lower, values)
643
+
644
+ return sorted(list(values), key=lambda x: str(x))
645
+
646
+ def _extract_values_from_detection(
647
+ self, detection, field_name_lower: str, values: set
648
+ ) -> None:
649
+ """
650
+ Recursively extract values from a detection for a given field name.
651
+ """
652
+ from sigma.rule import SigmaDetectionItem
653
+ from sigma.types import SigmaString, SigmaNumber
654
+
655
+ # Check if detection has detection_items attribute
656
+ if hasattr(detection, 'detection_items'):
657
+ for item in detection.detection_items:
658
+ self._extract_values_from_detection(item, field_name_lower, values)
659
+
660
+ # Check if this is a SigmaDetectionItem with field and value
661
+ if hasattr(detection, 'field') and hasattr(detection, 'value'):
662
+ # Check if the field matches (case-insensitive)
663
+ if detection.field and detection.field.lower() == field_name_lower:
664
+ # Extract the values
665
+ for value in detection.value:
666
+ # Handle different value types
667
+ if isinstance(value, SigmaNumber):
668
+ values.add(value.number)
669
+ elif isinstance(value, SigmaString):
670
+ # SigmaString - convert to plain string
671
+ values.add(str(value))
672
+ else:
673
+ values.add(str(value))
674
+
626
675
  def finalize_query_zircolite(
627
676
  self, rule: Union[SigmaRule, SigmaCorrelationRule], query: str, index: int, state: ConversionState
628
677
  ) -> Any:
629
678
  # For correlation rules, use the query as-is (already formatted)
630
679
  if isinstance(rule, SigmaCorrelationRule):
631
680
  sqlite_query = query
681
+ # Correlation rules don't have detection items in the same way
682
+ channels = []
683
+ event_ids = []
632
684
  else:
633
685
  sqlite_query = f"SELECT * FROM logs WHERE {query}"
686
+ # Extract channels and event IDs from the rule's detection
687
+ channels = self._extract_field_values_from_rule(rule, "Channel")
688
+ event_ids = self._extract_field_values_from_rule(rule, "EventID")
634
689
 
635
690
  rule_as_dict = rule.to_dict()
636
691
 
@@ -651,6 +706,8 @@ class sqliteBackend(TextQueryBackend):
651
706
  "level": rule_as_dict["level"] if "level" in rule_as_dict else "",
652
707
  "rule": [sqlite_query],
653
708
  "filename": "",
709
+ "channel": channels,
710
+ "eventid": event_ids,
654
711
  }
655
712
  return zircolite_rule
656
713