|
16 | 16 |
|
17 | 17 | class SumoLogicCSEBackend(TextQueryBackend): |
18 | 18 | """ |
19 | | - Sumo Logic Cloud SIEM (CSE) backend for converting Sigma rules to CSE Rule JSON format. |
| 19 | + Sumo Logic Cloud SIEM backend for converting Sigma rules to CSIEM Rule JSON format. |
20 | 20 |
|
21 | | - This backend converts Sigma rules into Sumo Logic Cloud SIEM Rule JSON format with: |
22 | | - - CSE-compatible query expressions |
| 21 | + This backend converts Sigma rules into Sumo Logic CSIEM Rule JSON format with: |
| 22 | + - Cloud SIEM-compatible query expressions |
23 | 23 | - MITRE ATT&CK technique and tactic mapping |
24 | 24 | - Risk score calculation based on severity |
25 | 25 | - Full rule metadata including name, description, and category |
26 | 26 | """ |
27 | 27 |
|
28 | | - name: ClassVar[str] = "Sumo Logic Cloud SIEM (CSE) Backend" |
| 28 | + name: ClassVar[str] = "Sumo Logic Cloud SIEM Backend" |
29 | 29 | formats: Dict[str, str] = { |
30 | | - "default": "Sumo Logic CSE Rule JSON format", |
31 | | - "cse_rule": "CSE Rule JSON with full metadata", |
| 30 | + "default": "Sumo Logic CSIEM Rule JSON format", |
| 31 | + "cse_rule": "CSIEM Rule JSON with full metadata", |
32 | 32 | } |
33 | 33 | requires_pipeline: bool = True |
34 | 34 |
|
35 | | - # CSE uses lowercase boolean operators |
| 35 | + # Cloud SIEM uses uppercase boolean operators |
36 | 36 | precedence: ClassVar[Tuple[ConditionItem, ConditionItem, ConditionItem]] = ( |
37 | 37 | ConditionNOT, |
38 | 38 | ConditionAND, |
39 | 39 | ConditionOR, |
40 | 40 | ) |
41 | 41 | group_expression: ClassVar[str] = "({expr})" |
42 | 42 |
|
43 | | - # CSE Query tokens - uppercase for CSE |
| 43 | + # Cloud SIEM Query tokens - uppercase operators |
44 | 44 | token_separator: str = " " |
45 | 45 | or_token: ClassVar[str] = "OR" |
46 | 46 | and_token: ClassVar[str] = "AND" |
@@ -492,8 +492,8 @@ def _inject_vendor_product_metadata(self, rule: SigmaRule, query: str) -> str: |
492 | 492 | import warnings |
493 | 493 | logsource_str = f"product={rule.logsource.product}, service={getattr(rule.logsource, 'service', None)}, category={getattr(rule.logsource, 'category', None)}" |
494 | 494 | warnings.warn( |
495 | | - f"No CSE parser mapping found for logsource: {logsource_str}. " |
496 | | - f"Rule may not match expected log sources in CSE." |
| 495 | + f"No Cloud SIEM parser mapping found for logsource: {logsource_str}. " |
| 496 | + f"Rule may not match expected log sources in Cloud SIEM." |
497 | 497 | ) |
498 | 498 | return query |
499 | 499 |
|
@@ -583,6 +583,61 @@ def _transform_windows_metadata_fields(self, rule: SigmaRule, query: str) -> str |
583 | 583 |
|
584 | 584 | return query |
585 | 585 |
|
| 586 | + def _wrap_vendor_specific_fields(self, rule: SigmaRule, query: str) -> str: |
| 587 | + """ |
| 588 | + Wrap vendor-specific fields in fields[] syntax based on logsource context. |
| 589 | +
|
| 590 | + Rules with specific product/service are vendor-specific and their unmapped fields |
| 591 | + should use fields[] syntax. Rules with only category use normalized field names. |
| 592 | +
|
| 593 | + Args: |
| 594 | + rule: Sigma rule object with logsource information |
| 595 | + query: Generated CSE query expression |
| 596 | +
|
| 597 | + Returns: |
| 598 | + Query with vendor-specific fields wrapped in fields[] syntax |
| 599 | + """ |
| 600 | + if not rule.logsource: |
| 601 | + return query |
| 602 | + |
| 603 | + # Determine if this is a vendor-specific logsource |
| 604 | + has_product = rule.logsource.product is not None |
| 605 | + has_service = rule.logsource.service is not None |
| 606 | + has_only_category = ( |
| 607 | + rule.logsource.category is not None |
| 608 | + and not has_product |
| 609 | + and not has_service |
| 610 | + ) |
| 611 | + |
| 612 | + # Only process vendor-specific logsources (not generic categories) |
| 613 | + if not (has_product or has_service) or has_only_category: |
| 614 | + return query |
| 615 | + |
| 616 | + # Find all bare field names (not already in fields[] syntax, not metadata fields) |
| 617 | + # Pattern: field name at word boundary, followed by operator (=, in, matches, etc.) |
| 618 | + # Exclude: metadata_, fields[, already wrapped fields |
| 619 | + pattern = r'\b(?!metadata_|fields\[)([a-zA-Z_][a-zA-Z0-9_]*)\b(?=\s*(?:=|!=|in\s|matches\s|<|>|<=|>=))' |
| 620 | + |
| 621 | + def wrap_if_not_in_schema(match): |
| 622 | + field_name = match.group(1) |
| 623 | + |
| 624 | + # Don't wrap if field is in CSE schema |
| 625 | + if self.schema and self.schema.field_exists(field_name): |
| 626 | + return field_name |
| 627 | + |
| 628 | + # Don't wrap boolean operators |
| 629 | + if field_name.upper() in ('AND', 'OR', 'NOT'): |
| 630 | + return field_name |
| 631 | + |
| 632 | + # Don't wrap CSE functions |
| 633 | + if field_name in ('isEmpty',): |
| 634 | + return field_name |
| 635 | + |
| 636 | + # Wrap vendor-specific field |
| 637 | + return f"fields['{field_name}']" |
| 638 | + |
| 639 | + return re.sub(pattern, wrap_if_not_in_schema, query) |
| 640 | + |
586 | 641 | def finalize_query_default( |
587 | 642 | self, rule: SigmaRule, query: str, index: int, state: ConversionState |
588 | 643 | ) -> str: |
@@ -623,6 +678,9 @@ def escape_regex_value(match): |
623 | 678 | # Transform Windows metadata fields to fields[] syntax |
624 | 679 | query = self._transform_windows_metadata_fields(rule, query) |
625 | 680 |
|
| 681 | + # Wrap vendor-specific fields based on logsource context |
| 682 | + query = self._wrap_vendor_specific_fields(rule, query) |
| 683 | + |
626 | 684 | # Inject vendor/product metadata based on logsource |
627 | 685 | query = self._inject_vendor_product_metadata(rule, query) |
628 | 686 |
|
@@ -1237,19 +1295,19 @@ def _determine_category_from_tags(self, mitre_tags: List[str]) -> str: |
1237 | 1295 |
|
1238 | 1296 | class SumoLogicCSERuleBackend(SumoLogicCSEBackend): |
1239 | 1297 | """ |
1240 | | - Sumo Logic CSE Rule Backend that outputs complete JSON rules. |
| 1298 | + Sumo Logic Cloud SIEM Rule Backend that outputs complete JSON rules. |
1241 | 1299 |
|
1242 | | - This backend extends SumoLogicCSEBackend to provide full CSE Rule JSON |
1243 | | - output suitable for direct import via CSE API or UI. |
| 1300 | + This backend extends SumoLogicCSEBackend to provide full CSIEM Rule JSON |
| 1301 | + output suitable for direct import via Cloud SIEM API or UI. |
1244 | 1302 | """ |
1245 | 1303 |
|
1246 | | - name: ClassVar[str] = "Sumo Logic CSE Rule JSON Backend" |
| 1304 | + name: ClassVar[str] = "Sumo Logic Cloud SIEM Rule JSON Backend" |
1247 | 1305 |
|
1248 | 1306 | def finalize_query_cse_rule( |
1249 | 1307 | self, rule: SigmaRule, query: str, index: int, state: ConversionState |
1250 | 1308 | ) -> str: |
1251 | 1309 | """ |
1252 | | - Finalize query as CSE Rule JSON for cse_rule format. |
| 1310 | + Finalize query as CSIEM Rule JSON for cse_rule format. |
1253 | 1311 | """ |
1254 | 1312 | rule_json = self.create_rule_json(rule, query) |
1255 | 1313 | self.rule_metadata.append(rule_json) |
|
0 commit comments