rsigma-convert

Convert Sigma rules into backend-native query strings. A rule is lowered to the intermediate representation (rsigma-ir) once, and the Backend trait renders from that HIR; ships with PostgreSQL/TimescaleDB, LynxDB, and Fibratus backends, and lets you add your own.

When to use

  • Generate SQL for an existing log store: PostgreSQL, TimescaleDB.
  • Generate SPL2 for LynxDB.
  • Build a custom backend (Elasticsearch DSL, Loki LogQL, ClickHouse SQL, Splunk SPL, KQL, anything that can be expressed as a text query). Implement Backend once and reuse the rule-walking machinery.

For event evaluation (running rules against in-memory events), use rsigma-eval.

Install

[dependencies]
rsigma-parser = "0.21.0"
rsigma-convert = "0.21.0"

No features. The crate is pure Rust + regex.

Public surface

Type Purpose
Backend trait The plug-in surface. Value leaves consume the faithful HIR (IrPattern, IrStrOp, IrNumber, RegexFlags, CompareOp) with no parser types; implement one leaf per matcher shape and return the query as a string.
TextQueryConfig ~90-field config struct that drives most text-query backends declaratively. Mirrors pySigma’s TextQueryBackend class variables (precedence, boolean operators, wildcards, string and field quoting, regex and CIDR templates, IN-list optimization, deferred parts, query envelope).
PostgresBackend The PostgreSQL/TimescaleDB backend. Output formats: default, view, timescaledb, continuous_aggregate, sliding_window.
LynxDbBackend The LynxDB backend. Output formats: default, minimal.
FibratusBackend The Fibratus backend. Output formats: default, expr, yaml, rule.
TextQueryTestBackend A backend-neutral text format used by the test suite and useful for debugging how a rule lowers to a generic boolean expression.
convert_collection(backend, &SigmaCollection, &[Pipeline], output_format) Convert a whole collection, applying pipelines per rule. Returns a ConversionOutput with per-rule queries and per-rule errors.
Backend::convert_rule(rule, output_format, &PipelineState) Lower-level single-rule entry point on the trait.
ConversionOutput, ConversionResult, ConversionState, PipelineState Collection/result wrappers, the per-condition conversion state (ConversionState), and the per-rule pipeline state (PipelineState from rsigma-eval).

The full Backend trait method list and the per-backend modifier mapping are in the crate README and in the PostgreSQL and LynxDB backend references.

Minimum example

use rsigma_convert::{convert_collection, backends::postgres::PostgresBackend};
use rsigma_parser::parse_sigma_yaml;

let rule_yaml = r#"
title: Whoami
id: 8b1d8c97-5b3a-4d77-9b48-7c5f7c8b1a2a
logsource: { product: windows, category: process_creation }
detection:
    selection:
        CommandLine|contains: 'whoami'
    condition: selection
"#;

let collection = parse_sigma_yaml(rule_yaml)?;
let backend = PostgresBackend::new();

let output = convert_collection(&backend, &collection, &[], "default")?;
for result in &output.queries {
    for q in &result.queries {
        println!("-- {}\n{}\n", result.rule_title, q);
    }
}
// -- Whoami
// SELECT * FROM security_events WHERE "CommandLine" ILIKE '%whoami%'

Backend options

PostgresBackend::from_options(&HashMap<String, String>) reads the CLI -O key=value map. Recognised keys:

Key Effect
table Default table name (default security_events).
schema PostgreSQL schema.
database Connection-level metadata for some output formats.
timestamp_field Column name for the timestamp (default time).
json_field Treat field references as JSONB extraction paths in this column.
case_sensitive_re Use ~ instead of ~* for regex.

LynxDB has no CLI -O options; its only knob is the target index, controlled via pipeline set_state with key: index (default main).

Writing a custom backend

The smallest viable backend implements Backend, returns a TextQueryConfig, and lets the trait’s default methods do the heavy lifting:

use rsigma_convert::{Backend, TextQueryConfig, TokenType};

pub struct MyBackend;

static MY_CONFIG: TextQueryConfig = TextQueryConfig {
    precedence: (TokenType::NOT, TokenType::AND, TokenType::OR),
    group_expression: "({expr})",
    token_separator: " ",
    and_token: "AND",
    or_token: "OR",
    not_token: "NOT",
    eq_token: " = ",
    not_eq_token: Some(" <> "),
    // ... 80+ other knobs, see the docs.rs page
    .. TextQueryConfig::PYSIGMA_DEFAULTS
};

impl Backend for MyBackend {
    fn name(&self) -> &str { "my_backend" }

    fn formats(&self) -> &[(&str, &str)] {
        &[("default", "Plain MyBackend query")]
    }

    fn text_query_config(&self) -> Option<&TextQueryConfig> {
        Some(&MY_CONFIG)
    }
}

The default convert_rule lowers the rule to IrRule, walks the resulting IrDetection and IrCondition, and dispatches into text_* helpers (e.g. text_convert_field_str_ir, text_convert_ir_pattern) that consult the config. Only override the leaves that your backend needs to behave differently from pySigma’s TextQueryBackend default.

See Adding Backends for the step-by-step walkthrough, the testing pattern, and how to wire a new backend into rsigma backend convert if you also want CLI integration.

Error handling

ConvertError from thiserror. Variants include RuleConversion (a rule could not be converted with the chosen backend or format), UnsupportedModifier, InvalidIdentifier (table/schema name failed validation), and Pipeline (a pre-conversion pipeline step failed).

See also