Adding a new backend🔗
The Backend trait in rsigma-convert is the plug-in surface for SIEM query generation. The shipped implementations are PostgresBackend, LynxDbBackend, and the two test backends; this page walks through adding your own (Splunk, Elastic, KQL, ClickHouse, …) and wiring it into the CLI.
Decide on the shape🔗
Two flavours of backend, depending on how much pySigma-style boilerplate you want to inherit:
- Text-query backend. Set
text_query_config()to aTextQueryConfigand let the trait's default methods walk the condition AST for you. This is howPostgresBackendandLynxDbBackendare built. Use this if your target language is a flat boolean expression withfield op valueshapes. - Custom backend. Override
convert_ruleoutright. Use this when your target language has fundamentally different structure (a tree-shaped JSON DSL like Elasticsearch query DSL, or a pipeline of stages like Splunk SPL).
Most SIEMs fit shape 1.
Walkthrough: a text-query backend🔗
Step 1: scaffold the crate module.
crates/rsigma-convert/src/backends/
├── lynxdb/
├── postgres/
├── splunk/ ← new
│ └── mod.rs
└── mod.rs ← register the new module here
Add pub mod splunk; to crates/rsigma-convert/src/backends/mod.rs.
Step 2: write the TextQueryConfig constant. The full schema lives on docs.rs/rsigma-convert. TextQueryConfig does not have a Default impl; the cleanest pattern is to copy crates/rsigma-convert/src/backends/postgres/mod.rs (the POSTGRES_CONFIG block at the top of the file) or lynxdb/mod.rs (the LYNX_CONFIG block) as a starting template and edit the operators, quoting, and templates to match your target language. The key fields you almost always need to set:
| Field | Example |
|---|---|
precedence | (TokenType::NOT, TokenType::AND, TokenType::OR) |
and_token, or_token, not_token | "AND", "OR", "NOT" |
eq_token | "=" (Splunk) or " = " (Postgres). |
group_expression | "({expr})" |
str_quote, escape_char | How to wrap and escape string literals. |
wildcard_multi, wildcard_single | "*", "?" for most SIEMs. |
re_expression, cidr_expression | Format strings for regex and CIDR comparisons. |
Run rustdoc (cargo doc --open -p rsigma-convert) for the full list of ~90 fields.
Step 3: implement the trait.
use rsigma_convert::{Backend, TextQueryConfig};
use rsigma_convert::error::Result;
use rsigma_eval::pipeline::ConversionState;
use rsigma_parser::SigmaRule;
pub struct SplunkBackend {
pub config: &'static TextQueryConfig,
pub index: String,
}
impl SplunkBackend {
pub fn new() -> Self {
Self {
config: &SPLUNK_CONFIG,
index: "main".to_string(),
}
}
pub fn from_options(options: &std::collections::HashMap<String, String>) -> Self {
let mut b = Self::new();
if let Some(v) = options.get("index") {
b.index = v.clone();
}
b
}
}
impl Backend for SplunkBackend {
fn name(&self) -> &str { "splunk" }
fn formats(&self) -> &[(&str, &str)] {
&[("default", "SPL search command"),
("savedsearch", "savedsearches.conf stanza")]
}
fn text_query_config(&self) -> Option<&TextQueryConfig> {
Some(self.config)
}
fn finalize_query(
&self,
rule: &SigmaRule,
query: String,
output_format: &str,
_state: &ConversionState,
) -> Result<String> {
match output_format {
"default" => Ok(format!("index={} | search {}", self.index, query)),
"savedsearch" => {
let name = rule.title.replace(' ', "_");
Ok(format!(
"[{name}]\nsearch = index={} | search {}\n",
self.index, query
))
}
_ => Err(rsigma_convert::ConvertError::RuleConversion(
format!("unknown output format: {output_format}"))),
}
}
}
Step 4: re-export from lib.rs so embedders can use the backend type directly.
Wire it into the CLI🔗
Open crates/rsigma-cli/src/commands/convert.rs. The get_backend function is a small match on the -t/--target string:
fn get_backend(target: &str, options: &HashMap<String, String>) -> Box<dyn Backend> {
match target {
"postgres" | "postgresql" | "pg" =>
Box::new(PostgresBackend::from_options(options)),
"lynxdb" =>
Box::new(LynxDbBackend::new()),
"splunk" => // ← add this
Box::new(SplunkBackend::from_options(options)),
"test" => /* ... */,
_ => /* fall through to unknown-target error */,
}
}
Update the Available targets: error message at the bottom of the function and the cmd_list_targets printer earlier in the same file so unknown targets and rsigma backend targets both include the new option.
Then run cargo install --path crates/rsigma-cli --force --features daemon and:
rsigma backend targets
# postgres, lynxdb, splunk, test
rsigma backend convert -t splunk -O index=security rule.yml
Test it🔗
Add an integration test under crates/rsigma-convert/tests/:
use rsigma_convert::{convert_collection, backends::splunk::SplunkBackend};
use rsigma_parser::parse_sigma_yaml;
#[test]
fn splunk_basic_keyword() {
let yaml = include_str!("fixtures/whoami.yml");
let collection = parse_sigma_yaml(yaml).unwrap();
let out = convert_collection(&SplunkBackend::new(), &collection, &[], "default").unwrap();
assert!(out.queries[0].queries[0].contains(r#"CommandLine="*whoami*""#));
}
Cover at least: keyword match, field=value, regex (re|), CIDR, IN-list (OR-folding), NULL, negation, and at least one correlation rule. The existing crates/rsigma-convert/tests/postgres.rs and lynxdb.rs files are the reference structure.
If your backend produces stable golden output, add a fixture under tests/fixtures/dynamic-pipelines/ and a comparison loop in CI; the existing Golden file comparison for rsigma pipeline resolve step in .github/workflows/ci.yml is the template.
Document it🔗
Three places to update:
- Per-backend reference page at
docs/reference/backends/<name>.md. Use the existing PostgreSQL backend reference as the template: modifier-mapping table, options table, output-format catalogue, examples. - CLI reference for
rsigma backend convertatdocs/cli/backend/convert.mdif your backend introduces new options. - Backend list page at
docs/reference/backends/.pages(if it exists) or themkdocs.ymlnav.
Checklist🔗
- Module added under
crates/rsigma-convert/src/backends/<name>/mod.rs. - Re-exported from
crates/rsigma-convert/src/lib.rs. -
Backendtrait implemented (orTextQueryConfigset). - CLI dispatch wired in
crates/rsigma-cli/src/commands/convert.rs. - Integration tests in
crates/rsigma-convert/tests/<name>.rs. - Backend reference page under
docs/reference/backends/<name>.md. - CHANGELOG entry.
See also🔗
rsigma-convertREADME for the fullBackendtrait surface and the existing pySigma-equivalent class variables.- Rule Conversion for the user-facing CLI flow.
- PostgreSQL backend reference and LynxDB backend reference for the two shipped reference implementations.