rsigma-convert

Convert parsed Sigma rules into backend-native query strings. Ships with PostgreSQL/TimescaleDB and LynxDB backends; the Backend trait 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.18.0"
rsigma-convert = "0.18.0"

No features. The crate is pure Rust + regex.

Public surface

Type Purpose
Backend trait The plug-in surface (~30 methods). Implement one method per detection-item shape, 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.
TestBackend 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, &ConversionState) Lower-level single-rule entry point on the trait.
ConversionOutput, ConversionResult, ConversionState Output-format-specific result wrapper, per-rule result, and the per-rule pipeline state used during conversion.

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 options today; 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 walks the condition AST and dispatches into text_* helpers (e.g. text_convert_field_eq_str, text_convert_field_eq_cidr) that consult the config. Only override the methods 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