Skip to content

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.12.0"
rsigma-convert = "0.12.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🔗