rsigma-parser
rsigma-parser is a parser for Sigma detection rules, correlations, and filters. It parses Sigma YAML into a strongly-typed AST covering the full Sigma 2.0 specification, and includes a 90-rule linter derived from the Sigma v2.1.0 spec.
This library is part of rsigma.
Public API
Parsing
| Function | Description |
|---|---|
parse_sigma_yaml(yaml: &str) |
Parse a multi-document YAML string into a SigmaCollection |
parse_sigma_file(path: &Path) |
Parse a single YAML file |
parse_sigma_directory(dir: &Path) |
Recursively parse all .yml/.yaml files in a directory |
parse_condition(input: &str) |
Parse a condition expression string into a ConditionExpr |
parse_field_spec(key: &str) |
Parse a field specification like "CommandLine|contains|all" into a FieldSpec |
Validation
The parser runs these checks on every detection item, and rsigma-ir reruns them when it lowers a rule that code or a processing pipeline may have rewritten.
| Function | Description |
|---|---|
validate::check_detection_item(item: &DetectionItem) |
Check modifiers, value types, regular expressions, CIDR networks, and exists values of one detection item |
validate::check_modifiers(modifiers: &[Modifier]) |
Reject conflicting modifiers, such as two operators or two UTF-16 encodings |
validate::check_regex(pattern: &str) |
Reject an invalid regular expression; lookaround and backreferences are accepted, as in pySigma |
validate::check_cidr(cidr: &str) |
Reject a CIDR network that is not address/prefix or has host bits set |
validate::exists_flag(value: &SigmaValue) |
Return an exists value when it is a boolean, or None for every other type |
Emitting
| Function | Description |
|---|---|
emit_rule_yaml(rule: &SigmaRule) |
Emit a rule as canonical Sigma YAML (the inverse of parse_sigma_yaml) |
emit_collection_yaml(collection: &SigmaCollection) |
Emit every detection rule in a collection, separated by --- |
The emitter is a deterministic canonical form: named detections, logsource custom fields, and custom attributes are emitted in sorted order, field|modifier keys are reconstructed from the FieldSpec, and literal wildcards in values are escaped so a re-parse reproduces the same SigmaString (while re/cidr/fieldref values stay raw). It powers rsigma-convert's reverse conversion (query to Sigma YAML). Sequences of mappings, including rsigma.exemplars, round-trip through emit.
Exemplars
| Function | Description |
|---|---|
exemplars(rule) / correlation_exemplars(rule) / filter_exemplars(rule) |
Extract typed rsigma.exemplars from a parsed rule |
parse_exemplars(value, kind, path) |
Parse a raw YAML value with kind-specific payload rules |
raw_exemplar_values(mapping) |
Both nested and top-level placements, with JSON-pointer paths for lint |
match_exemplar_count(attrs) |
Count structurally valid expect: match exemplars (ADS validation presence) |
Linting
| Function | Description |
|---|---|
lint_yaml_value(value: &Value) |
Lint a single YAML document value (auto-detects document type) |
lint_yaml_str(text: &str) |
Lint raw YAML string with source span resolution |
lint_yaml_str_with_config(text: &str, config: &LintConfig) |
Lint with config-based suppression |
lint_yaml_file(path: &Path) |
Lint all documents in a file |
lint_yaml_file_with_config(path: &Path, config: &LintConfig) |
Lint a file with config |
lint_yaml_directory(dir: &Path) |
Recursively lint all .yml/.yaml in a directory |
lint_yaml_directory_with_config(dir: &Path, config: &LintConfig) |
Lint a directory with config |
parse_inline_suppressions(text: &str) |
Parse # rsigma-disable comments from YAML text |
apply_suppressions(warnings, config, inline) |
Filter and override warnings using config and inline suppressions |
Specification Version
A document may declare the Sigma specification major it targets via the top-level sigma-version attribute; version-sensitive syntax (array-matching brackets) is gated on it.
| Function / Constant | Description |
|---|---|
version::resolve_major(declared) |
Resolve a sigma-version major, defaulting to the fixed floor when absent |
version::array_matching_enabled(declared) |
Whether array-matching bracket selectors are active at the resolved major |
version::is_unsupported(declared) |
Whether a declared major exceeds what this build supports |
SPEC_VERSION_FLOOR / SPEC_VERSION_ARRAY_MATCHING / SPEC_VERSION_SUPPORTED |
The floor (2), array-matching (3), and highest supported majors |
Auto-Fix Types
Each LintWarning can carry an optional Fix describing how to automatically correct the issue.
| Type | Description |
|---|---|
Fix |
A suggested fix: title, disposition (Safe/Unsafe), and ordered patches |
FixDisposition |
Safe (no semantic change) or Unsafe (may change meaning) |
FixPatch |
A single edit: ReplaceValue, ReplaceKey, or Remove with a JSON-pointer path |
The lint::fix module turns these into concrete edits on a YAML source string while preserving comments and formatting:
| Function / Type | Description |
|---|---|
lint::fix::apply_fixes_to_source(source: &str, &[&LintWarning]) -> SourceFixOutcome |
Apply every Safe fix to a YAML string; returns the rewritten source plus applied/failed counts |
lint::fix::json_pointer_to_route / apply_single_fix_patch / apply_rename_key |
Lower-level helpers over yamlpath/yamlpatch |
SourceFixOutcome |
{ fixed_source, applied, failed } |
The CLI (rule lint --fix), the LSP, and the MCP server's fix_rules tool all share this one implementation.
The auto-fix implementation is enabled by the default fix feature. Disable default features when only parsing and lint diagnostics are needed, including for wasm32-unknown-unknown builds; this omits the tree-sitter-based yamlpath/yamlpatch dependencies and the lint::fix module.
Lint Catalogue
lint::catalogue::catalogue() -> Vec<LintRuleInfo> returns programmatic metadata for every lint rule (stable id, default severity, fix disposition, one-line description). It is generated from a single list whose exhaustive match makes adding a LintRule variant without a catalogue entry a compile error.
Reference Data
reference::MODIFIERS and reference::MITRE_TACTICS are (name, description) tables for the field modifiers and MITRE ATT&CK tactics, shared with the LSP (hover/completion) and the MCP server (reference resources).
Value Types
| Type/Function | Description |
|---|---|
SigmaString::new(s: &str) |
Parse a string with wildcard interpretation (*, ?, \ escape) |
SigmaString::from_raw(s: &str) |
Create from raw string (no wildcard parsing; used for |re modifier) |
SigmaString::is_plain() |
Returns true if no wildcards |
SigmaString::contains_wildcards() |
Returns true if any wildcard present |
SigmaString::as_plain() |
Get plain string; None if wildcards present |
SigmaValue::from_yaml(v: &Value) |
Create from a YAML value |
Timespan::parse(s: &str) |
Parse timespan like 1h, 15s, 7d |
Parsing
- Multi-document YAML:
---separators,action: global/reset/repeatfor rule templates - Condition expressions: PEG grammar (pest) with Pratt parsing and correct operator precedence (
NOT>AND>OR). Supportsand,or,not,1 of,all of,any of,N of, parenthesized groups, wildcard patterns —themexcludes_-prefixed identifiers per spec - Value types: strings with wildcards (
*,?), escape sequences (\*,\?,\\), integers, floats, booleans, null - Timespan parsing:
15s,30m,1h,7d,1w,1M,1y - Logsource:
category,product,service,definition, custom fields. Detection and filter rules must have alogsourcethat sets at least one ofcategory,product, orservice. - Semantic validation: conflicting modifiers, values of the wrong type for their modifiers, invalid regular expressions and CIDR networks, empty detections, and conditions that reference undefined detections are parse errors, as in pySigma
Multi-Document Behavior
yaml_serde::Deserializeryields documents separated by---.- Non-mapping documents are skipped; errors are accumulated in
collection.errors. - YAML parse errors stop iteration (the deserializer may not recover from malformed input).
- Collection actions:
action: global— store document as a template; removeactionkey; do not produce a rule.action: reset— clear the global template.action: repeat— merge current document onto the previous document; apply global template if present; parse the merged result. Error if no previous document exists.
- Merge order: For normal documents:
merged = deep_merge(global, value). For repeat:merged = deep_merge(global, deep_merge(previous, repeat_doc)). deep_merge: Recursive. Source mappings override destination keys; non-mapping source replaces destination entirely.- Previous tracking: Updated after each non-action document and after each repeat. Repeat chains from the last document, not the original.
SigmaString Escape Semantics
| Input | Parsed as |
|---|---|
\* |
literal * (not a wildcard) |
\? |
literal ? (not a wildcard) |
\\ |
literal \ |
\W (non-special) |
literal \W (both characters kept) |
Backslash only consumes itself when followed by *, ?, or \. This preserves Windows paths like \Windows\System32.
Condition Expression Grammar (PEG)
The full PEG grammar is defined in src/sigma.pest. It implements the Sigma condition expression syntax using a Pratt parser with not > and > or precedence.
Parsing quirks:
!ident_charlookahead ensuresand_filterparses as a single identifier, notand+filter.1 ofandany ofboth map toQuantifier::Any.- Nested same-type binary ops are flattened:
a and b and cbecomesAnd([a, b, c]), notAnd(a, And(b, c)).
Operator Precedence
| Precedence (highest first) | Operator | Associativity |
|---|---|---|
| 1 | not (prefix) |
— |
| 2 | and (infix) |
Left |
| 3 | or (infix) |
Left |
a or not b and c parses as a or ((not b) and c).
AST Types
Core Types
| Type | Description |
|---|---|
SigmaCollection |
Collection of rules, correlations, filters, and errors |
SigmaRule |
A parsed detection rule with metadata, an optional sigma_version (the targeted spec major), logsource, and detections |
CorrelationRule |
A correlation rule with type, referenced rules, timespan, window mode, and conditions |
FilterRule |
A filter rule that injects AND NOT conditions into referenced rules |
Detections |
Named detections, condition expressions, and optional timeframe |
Detection |
AllOf (AND-linked items), AnyOf (OR-linked), Keywords (plain values), ArrayMatch (object-scope array block), And, or Conditional (extended array block) |
FieldSpec |
Field name + modifier chain; name is None for keyword detections |
ConditionExpr |
And, Or, Not, Identifier, or Selector with quantifier and pattern |
SigmaValue |
String, Integer, Float, Bool, or Null |
SigmaString |
String with wildcard parts (Plain text + WildcardMulti/WildcardSingle) |
Enums
| Enum | Variants |
|---|---|
Status |
Stable, Test, Experimental, Deprecated, Unsupported |
Level |
Informational, Low, Medium, High, Critical |
RelationType |
Derived, Obsolete, Merged, Renamed, Similar |
Quantifier |
Any, All, Count(u64) |
ArrayQuantifier |
Any, All, AllOrEmpty, None (array object-scope quantifiers) |
SelectorPattern |
Them, Pattern(String) |
CorrelationType |
EventCount, ValueCount, Temporal, TemporalOrdered, ValueSum, ValueAvg, ValuePercentile, ValueMedian |
Detection Parsing
- YAML mapping →
Detection::AllOf(AND-linked items) - YAML list of mappings →
Detection::AnyOf(OR-linked) - YAML list of plain values →
Detection::Keywords(keyword search across all fields) - Condition as list (
condition: [s1, s2]) → multipleConditionExprparsed independently - Empty field name (
parse_field_spec("")) →FieldSpec { name: None, modifiers: [] }(keyword) - Array selectors (
field[any],field[all],field[N]) → desugared on the field path only when the document targetssigma-version: 3or higher; below that they are literal field-name characters. See the Array Matching guide
Field Modifiers (30)
The parser recognizes 30 modifier variants, some with aliases:
| Category | Modifiers | Aliases |
|---|---|---|
| String matching | contains, startswith, endswith |
— |
| Value linking | all |
— |
| Encoding | base64, base64offset, wide, utf16be, utf16, windash |
utf16le → wide |
| Pattern | re, cidr |
— |
| Case | cased |
— |
| Existence | exists |
— |
| Placeholder | expand |
— |
| Field reference | fieldref |
May be followed by one of contains, startswith, or endswith, and by neq. The value must not contain wildcards. |
| Numeric comparison | gt, gte, lt, lte |
— |
| Inequality | neq |
Negates the whole item, so a list matches when the field equals none of the values. Combines with string modifiers, re, cidr, and fieldref. |
| Regex flags | i, m, s |
ignorecase → i, multiline → m, dotall → s |
| Timestamp parts | minute, hour, day, week, month, year |
— |
Each modifier may appear once per key; a repeated modifier such as field|neq|neq is a DuplicateModifier error.
When the re modifier is present, string values are parsed with SigmaValue::from_raw_string (no wildcard interpretation).
Correlation Rules (8 types)
| Type | Description |
|---|---|
event_count |
Count matching events per group key |
value_count |
Count distinct field values per group key |
temporal |
Require multiple rules to fire in the same window |
temporal_ordered |
Same as temporal, but rules must fire in order |
value_sum |
Sum a numeric field across events |
value_avg |
Average a numeric field across events |
value_percentile |
Compute a percentile of a numeric field |
value_median |
Compute the median of a numeric field |
Correlation Conditions
- Threshold (mapping):
{ gte: 100 }or{ gt: 10, lte: 100, field: "TargetUser" }. Operators:lt,lte,gt,gte,eq,neq. Values must be numeric. - Extended (string):
"rule_a and rule_b"for temporal types — parsed as a boolean expression over rule references. - Default (temporal, no condition):
Threshold { predicates: [(Gte, N)], field: None }, whereNis the number of distinct entries inrules, so every referenced rule must match within the timespan. A temporal correlation with neither a condition nor any rules is a parse error. - Timeframe/timespan: The parser accepts both
timeframeandtimespankeys. - Custom attributes: Both detection and correlation rules expose a unified
custom_attributesmap (HashMap<String, yaml_serde::Value>). It merges (a) any arbitrary top-level YAML key that is not part of the Sigma schema, (b) entries of the optional top-levelcustom_attributes:mapping (explicit block wins over arbitrary keys of the same name), and (c) values set by pipelineSetCustomAttributetransformations (applied last, last-write-wins). Engines readrsigma.*extensions (rsigma.suppress,rsigma.action,rsigma.correlation_event_mode, etc.) from this map.
Window Modes
Correlation rules accept an optional window attribute that controls how timespan is anchored to the event stream, plus a gap for session windows:
sliding(default): trailing per-event window(t - timespan, t]. Omittingwindowis equivalent and preserves existing behavior.tumbling: fixed, boundary-aligned, non-overlapping buckets of sizetimespan.session: dynamic window that extends while consecutive in-group events stay withingap, capped bytimespanas the maximum total span.gapis required forsessionand must not be set for the other modes.
This is an rsigma extension (a portable-spec version was declined upstream), so the primary spelling is the rsigma.* engine-extension namespace, with the bare keys kept as aliases:
- Primary: top-level
rsigma.window/rsigma.gap(alongsidersigma.suppress,rsigma.action). - Alias: nested
correlation.window/correlation.gap.
The rsigma.* spelling wins when both are present. Either spelling maps to CorrelationRule::window (a WindowMode of Sliding/Tumbling/Session) and CorrelationRule::gap (an Option<Timespan>), so consumers read one place regardless of how the rule was written.
Filter Rules
Filter rules inject exclusion conditions into referenced detection rules — enables centralized tuning without modifying original rule files. See the Sigma Filters Specification for the full standard.
Per the spec, selection, condition, and rules all live inside the filter section:
title: Exclude Admin Users
logsource:
category: process_creation
product: windows
filter:
rules:
- <rule-id>
selection:
User|startswith: 'adm_'
condition: selection
Timespan Parsing
| Unit | Suffix | Multiplier (seconds) |
|---|---|---|
| Second | s |
1 |
| Minute | m |
60 |
| Hour | h |
3,600 |
| Day | d |
86,400 |
| Week | w |
604,800 |
| Month | M (uppercase) |
2,629,746 (~30.44 days) |
| Year | y |
31,556,952 (~365.25 days) |
The string must be at least 2 characters (e.g. 1h). The last character is the unit; the prefix must be a positive integer.
Linter (90 rules)
90 emitted lint rules (plus the reserved empty_filter_rules) derived from the Sigma v2.1.0 specification, including the opt-in ADS detection-strategy checks. Four severity levels: Error (spec violation), Warning (best-practice issue), Info (soft suggestion), Hint (stylistic). Info/Hint findings don't cause lint failure.
The linter operates on raw YAML values to catch issues the parser silently ignores.
Infrastructure (4)
| Rule | Severity | Fix | Trigger |
|---|---|---|---|
yaml_parse_error |
Error | YAML parse failure | |
not_a_mapping |
Error | Document is not a YAML mapping | |
file_read_error |
Error | Cannot read file | |
schema_violation |
Error | JSON schema validation failure (optional) |
Shared Metadata (16)
| Rule | Severity | Fix | Trigger |
|---|---|---|---|
missing_title |
Error | No title field |
|
empty_title |
Error | title is empty or whitespace |
|
title_too_long |
Warning | title exceeds 256 characters |
|
missing_description |
Info | No description |
|
missing_author |
Info | No author |
|
invalid_id |
Warning | id not a valid UUID (8-4-4-4-12 hex) |
|
invalid_status |
Error | Yes | status not in stable/test/experimental/deprecated/unsupported |
missing_level |
Warning | No level (detection rules) |
|
invalid_level |
Error | Yes | level not in informational/low/medium/high/critical |
invalid_date |
Error | date not YYYY-MM-DD with valid day-of-month |
|
invalid_modified |
Error | modified not YYYY-MM-DD |
|
modified_before_date |
Warning | modified is earlier than date |
|
description_too_long |
Warning | description exceeds 65,535 characters |
|
name_too_long |
Warning | name exceeds 256 characters |
|
taxonomy_too_long |
Warning | taxonomy exceeds 256 characters |
|
non_lowercase_key |
Warning | Yes | Top-level key is not lowercase |
Detection Rules (19)
| Rule | Severity | Fix | Trigger |
|---|---|---|---|
missing_logsource |
Error | No logsource |
|
missing_detection |
Error | No detection |
|
missing_condition |
Error | No condition in detection |
|
empty_detection |
Warning | No named search identifiers | |
invalid_related_type |
Error | related[].type not in derived/obsolete/merged/renamed/similar |
|
invalid_related_id |
Warning | related[].id not a valid UUID |
|
related_missing_required |
Error | related[] missing id or type |
|
deprecated_without_related |
Warning | status: deprecated but no related |
|
invalid_tag |
Warning | Tag doesn't match ^[a-z0-9_-]+\.[a-z0-9._-]+$ |
|
unknown_tag_namespace |
Warning | Tag namespace not in attack/car/cve/d3fend/detection/stp/tlp |
|
duplicate_tags |
Warning | Yes | Duplicate tag |
duplicate_references |
Warning | Yes | Duplicate reference URL |
duplicate_fields |
Warning | Yes | Duplicate field name |
falsepositive_too_short |
Warning | falsepositives entry under 2 characters |
|
scope_too_short |
Warning | scope entry under 2 characters |
|
logsource_value_not_lowercase |
Warning | Yes | Logsource category/product/service not lowercase |
condition_references_unknown |
Error | Condition references non-existent detection identifier | |
deprecated_aggregation_syntax |
Warning | Condition uses deprecated Sigma v1.x pipe-aggregation syntax (| count/min/max/avg/sum/near); use a correlation rule instead |
|
deprecated_detection_timeframe |
Warning | timeframe inside detection is deprecated Sigma v1.x syntax and has no effect; use a correlation rule with a timespan instead |
Correlation Rules (17)
| Rule | Severity | Fix | Trigger |
|---|---|---|---|
missing_correlation |
Error | No correlation or not a mapping |
|
missing_correlation_type |
Error | No correlation.type |
|
invalid_correlation_type |
Error | Type not a recognized correlation type | |
missing_correlation_rules |
Error | No correlation.rules |
|
empty_correlation_rules |
Warning | correlation.rules is empty |
|
missing_correlation_timespan |
Error | No correlation.timespan or correlation.timeframe |
|
invalid_timespan_format |
Error | Timespan format invalid | |
invalid_window_mode |
Error | correlation.window not sliding/tumbling/session |
|
missing_session_gap |
Error | window: session without a gap |
|
gap_without_session |
Error | gap set without window: session |
|
invalid_gap_format |
Error | gap format invalid |
|
missing_group_by |
Error | No correlation.group-by |
|
missing_correlation_condition |
Error | Non-temporal type without condition | |
missing_condition_field |
Error | value_count/value_sum/value_avg/value_percentile without condition.field |
|
invalid_condition_operator |
Error | Operator not in gt/gte/lt/lte/eq/neq |
|
condition_value_not_numeric |
Error | Condition value not numeric | |
generate_not_boolean |
Error | generate is not a boolean |
|
correlation_only_references |
Info | Referenced rules produce no standalone output unless generate is true |
Filter Rules (9 IDs, 8 emitted)
| Rule | Severity | Fix | Trigger |
|---|---|---|---|
missing_filter |
Error | No filter or not a mapping |
|
missing_filter_rules |
Error | No filter.rules |
|
empty_filter_rules |
reserved | Declared in the enum but not emitted in production today | |
missing_filter_selection |
Error | No filter.selection |
|
missing_filter_condition |
Error | No filter.condition |
|
filter_has_level |
Warning | Yes | Filter has level (not applicable) |
filter_has_status |
Warning | Yes | Filter has status (not applicable) |
missing_filter_logsource |
Error | No logsource |
|
filter_reference_by_title |
Warning | A filter references a rule by title instead of id or name |
Detection Logic (8)
| Rule | Severity | Fix | Trigger |
|---|---|---|---|
null_in_value_list |
Warning | null mixed with other values in a list |
|
single_value_all_modifier |
Warning | Yes | |all with a single value |
all_with_re |
Warning | Yes | |all and |re combined |
incompatible_modifiers |
Warning | Incompatible modifier combination (e.g. contains|startswith, re|contains, gt|contains, regex flags without re) |
|
empty_value_list |
Warning | Empty value list | |
wildcard_only_value |
Warning | Yes | Lone * value (suggests |exists: true instead) |
flattened_array_correlation |
Warning | Sibling keys share a quantified array prefix (e.g. connections[any].protocol and connections[any].ip); they open independent scopes and don't correlate on one element, so an object-scope block is needed |
|
unknown_key |
Info | Yes | Top-level key likely a typo of a known key (edit distance ≤ 2); custom fields are allowed per the Sigma spec |
Specification Version and References (4)
| Rule | Severity | Fix | Trigger |
|---|---|---|---|
unsupported_sigma_version |
Error | sigma-version declares a major newer than this build supports |
|
array_matching_without_version |
Warning | Array-matching bracket syntax used below sigma-version: 3 (brackets read literally) |
|
sigma_version_mismatch |
Warning | A correlation/filter and a rule it references declare different majors | |
unknown_rule_reference |
Warning | A correlation.rules/filter.rules entry resolves to no rule (directory linting only, where the index is complete) |
Exemplars (2)
| Rule | Severity | Fix | Trigger |
|---|---|---|---|
exemplar_shape |
Warning | Invalid rsigma.exemplars structure |
|
exemplar_wrong_rule_kind |
Warning | event/events does not match the host rule kind, or a filter carries exemplars |
Rule Suppression
Three-tier system to disable or override lint rules:
- CLI:
--disable rule1,rule2suppresses specific rules globally - CLI:
--exclude "pattern"excludes paths matching glob patterns (relative to lint root, repeatable) - Config file:
.rsigma-lint.yml(or.rsigma-lint.yaml) withdisabled_rules,severity_overrides, andexclude, auto-discovered by walking ancestor directories from the target path upward - Inline comments:
# rsigma-disable,# rsigma-disable rule1, rule2,# rsigma-disable-next-line,# rsigma-disable-next-line rule1, rule2
# .rsigma-lint.yml
disabled_rules:
- missing_description
- missing_author
severity_overrides:
title_too_long: info
exclude:
- "config/**"
- "**/unsupported/**"
Suppression order: Excluded paths are skipped during directory traversal. Remaining warnings are filtered by disabled_rules, then by inline suppressions, then severity_overrides are applied.
Inline # inside quoted YAML strings is not treated as a comment.
Optional Schema Validation
The schema_violation lint rule optionally validates rules against a JSON schema. The schema can be the official Sigma schema (downloaded and cached for 7 days, with offline fallback to stale cache) or a local file.
Directory Parsing and Linting
parse_sigma_directory: Recursively walks directories; only processes.yml/.yamlfiles. File-level parse errors are accumulated incollection.errors(not fatal). Sub-collections (rules, correlations, filters) are merged.lint_yaml_directory: Skips hidden directories (.prefix). Canonicalizes paths to detect symlink cycles. Sorts entries by path for deterministic output. Runs a two-pass directory-global rule index so cross-document reference checks (sigma_version_mismatch,unknown_rule_reference) resolve rules (byidorname) across sibling files.
Error Types
| Error | When |
|---|---|
Yaml |
yaml_serde parse failure |
Condition |
Condition expression parse failure (PEG/Pratt); carries optional SourceLocation with line/column |
UnknownModifier |
Unknown modifier in field spec |
DuplicateModifier |
The same modifier appears more than once in a field spec |
NotIsNotAModifier |
The literal string |not was used as a modifier; Sigma expresses negation at the condition level (condition: not selection) or via |neq for inequality. Surfaced with guidance on how to rewrite the rule |
InvalidFieldSpec |
Invalid field specification |
InvalidRule |
Document not a mapping, or invalid structure |
MissingField |
Required field missing (e.g. title, detection) |
InvalidDetection |
Detection section invalid |
InvalidCorrelation |
Correlation rule invalid |
InvalidTimespan |
Timespan string invalid (wrong format, unknown unit) |
InvalidValue |
Invalid value in detection |
InvalidAction |
Unknown collection action |
Io |
File read error |
Compatibility
Tested against the SigmaHQ/sigma rule repository:
| Corpus | Rules Parsed | Errors |
|---|---|---|
rules/ |
3,110 | 0 |
rules-emerging-threats/ |
436 | 0 |
rules-threat-hunting/ |
133 | 0 |
rules-compliance/ |
3 | 0 |
rules-placeholder/ |
14 | 0 |
unsupported/ |
31 | 58 (deprecated pipe syntax) |
deprecated/ |
165 | 1 (deprecated pipe syntax) |
| Total | 3,892 | 0 real errors |
The deprecated pipe aggregation syntax (selection | count(field) by field > N) is intentionally rejected, matching pySigma behavior.
Usage
use ;
let collection = parse_sigma_yaml.unwrap;
assert_eq!;
// Parse a condition expression directly
let expr = parse_condition.unwrap;
Benchmarks
Criterion.rs benchmarks with synthetic rules (Apple M-series, single-threaded):
| Scenario | Time |
|---|---|
| 1 rule | 11.7 us |
| 100 rules | 1.1 ms |
| 1,000 rules | 11.1 ms |
| Complex condition (8 selections, nested booleans) | 23.2 us |
License
MIT License.