v0.20.0
TL;DR
RSigma v0.20.0 is the "intermediate representation, reverse conversion, and rstix STIX closure" release: a new HIR crate becomes the compile and convert backbone, SIEM queries convert back to Sigma YAML, rstix gains a TAXII 2.1 client alongside graph, marking, and store modules, and the schema classifier learns the major cloud log sources.
- Intermediate representation: a new
rsigma-irHIR crate withlower_rule, a serializable HIR restart cache, and opt-in optimization passes, withrsigma-evalcompile and thersigma-convertBackendtrait now IR-native end to end while backend output stays byte-identical (#360, #362, #363, #364, #368, #370). - Reverse conversion: a pluggable framework that parses a SIEM query dialect into the IR, raises it to a Sigma rule, and emits canonical YAML, with Elastic Lucene as the reference frontend, exposed through
rsigma rule reverseand the MCPreverse_converttool (#348, #371). rstixthreat intel: an async TAXII 2.1 client with mTLS, pagination, auth, and resilience (#373); spec-exact STIX 2.1 closure with graph traversal, TLP marking resolution, and an object store (#327); and serde-gated wire-format validator dependencies (#352), thanks to @SecurityEnthusiast.- Detection routing: a built-in cloud schema signature bundle covering AWS CloudTrail and VPC Flow, Azure, GCP, Microsoft 365, GitHub, Okta, OneLogin, Kubernetes, Docker, and osquery (#318).
- Reliability: deterministic bloom pre-filter seeding, so the per-field substring pre-filter's bit layout is stable across runs and platforms with correctness unchanged.
- Dependencies: two rolled-up Dependabot batches across the Rust workspace, CI actions, and the VS Code extension (#354, #357).
rstix: TAXII 2.1 Client (taxii feature) (#373)
taxii— async HTTP client for TAXII 2.1 endpoint groups (discovery, API Root, collections, objects, manifest, versions, status). Channels (§6) are RESERVED in the spec and not implemented. Includes DNS SRV via_taxii2._tcpandTaxiiClientConfig::dns_nameserver()for custom resolvers.- Wire format —
TaxiiEnvelopefor object exchange (notBundle);Accept/Content-Type: application/taxii+json;version=2.1on every request; manifest requests also accept STIX JSON; trailing-slash URL rules; HTTPS enforced by default (allow_insecure_httpfor tests);max_content_lengthenforced before POST. - Pagination —
TaxiiPaged<T>returns page bodies plusX-TAXII-Date-Added-First/Lastheaders; cursor + header fallback on objects, manifest, object-by-id, and versions streams. - Auth —
BearerAuth,BasicAuth,ApiKeyHeaderviaTaxiiAuthProvider; secrets usesecrecy::SecretStringwith redactedDebug. - Resilience —
RetryPolicy(503/429/network with backoff, honoringRetry-Afteron 503/429); fullTaxiiErrormapping including HTTP 415; optionalPreflightPolicyfor client-side permission guards. - mTLS —
ClientCertificate::from_pemandClientCertificate::from_pkcs12_der(pure-Rust PKCS#12 viap12-keystore) embedded inbuild_rustls_config; single rustls TLS stack. - Live harness fixes — DANE TLSA prefetch honors
dns_nameserver(). - Tests —
cargo test -p rstix --features taxii --test taxii_client. Optional live harness:tests/taxii-live/README.md(TLS, mTLS, PKCS#12 mTLS, SRV, TLS 1.3, DANE). - Conformance — TLS 1.2+, required success
Content-Type, strict pagination headers, RFC 2782 SRV weighted selection, HTTP 416 stream recovery, HTTPDateclock skew foradded_after,WWW-Authenticateparsing, API Root/collection capability checks, POST auto-poll (PostSubmitPolicy), optional SPKI pinning and DANE (ServerTrustPolicy),TaxiiDiscovery::default_api_root(), optionalStatusDetail.version.
Reverse conversion: SIEM queries to Sigma YAML (#348, #371)
A pluggable reverse-conversion framework, the mirror of the forward Backend engine: a query dialect is parsed into the intermediate representation, raised to a Sigma rule, and emitted as YAML. Elastic Lucene ships as the reference frontend.
rsigma-parser— newemit_rule_yaml/emit_collection_yaml, the inverse ofparse_sigma_yaml. The emitter is a deterministic canonical form (named detections, logsource custom fields, and custom attributes are sorted), reconstructsfield|modifierkeys, escapes literal wildcards in values while leavingre/cidr/fieldrefvalues raw, and renders the condition fromConditionExpr. An empty logsource is emitted as{}so it re-parses.rsigma-ir— newraise_rule(IrRuletoSigmaRule), the inverse oflower_rule: it reconstructs the modifiers eachIrMatchervariant implies, collapses homogeneous value lists, keeps conditions selector-preserving, and rejects numeric dynamic-source references. The canonicalir_pattern_to_sigmamoved here (re-exported fromrsigma-convert).rsigma-convert— newreversemodule: aFrontendtrait plus aQueryDialecttable drive a shared tokenizer and precedence-climbing boolean parser, andassemble_ruleturns a boolean tree of leaves into named Sigma selections and a condition (AND-merged selections, same-field OR value lists, negated branches as filters).reverse_collectionconverts a batch, collecting per-query errors. TheLuceneFrontendparses the Lucene / Elasticsearchquery_stringsubset (field:valuewith wildcards, quoted phrases,/regex/,[a TO b]/{a TO b}ranges, comparison shorthand,field:(a OR b)groups,_exists_, keyword terms, andAND/OR/NOTwith grouping) and rejects boosting, fuzzy/proximity, and non-numeric ranges with a structured error.rsigmaCLI — newrsigma rule reverse --from <dialect>(a single entry point for every dialect, mirroringbackend convert --target) reads queries from an inline argument, from--file(repeatable, files or directories: each file is one query, a directory contributes every query file it holds), or from stdin, takes--title/--id/--level/--statusand--logsource-*hints a query cannot carry, and prints Sigma YAML (or writes it with-o; a directory-oreceives one<name>.ymlper query, batch runs title each rule from its file name). Each emitted rule is parsed back before printing, so a rule that would not round-trip never reaches the operator.rsigma-mcp— newreverse_converttool (with adialectparameter) exposes reverse conversion over MCP, returning the drafted YAML or an error envelope for inexpressible constructs (13 tools total).
Deterministic bloom pre-filter seeding
The engine's per-field substring bloom pre-filter seeded its hasher from ahash's runtime RNG, so its exact bit layout, and therefore which non-matching values landed as false positives, varied across process runs (surfacing as a flaky bloom_index unit test). The filter now uses fixed seeds and is deterministic across runs and platforms. Correctness is unchanged: the bloom only ever over-approximates, never producing a false negative, regardless of seed.
Intermediate representation crate and IR-backed compile (#360, #362, #363, #364, #368, #370)
rsigma-ir— new sync-only crate with HIR types (IrRule,IrDetection,IrMatcher,IrCondition,IrCorrelation,IrFilter) andlower_rule/lower_*that resolve modifiers before eval. Quantified selectors are preserved so evaluation stays count-based. The matcher model is faithful and lossless: string matches keep a wildcard-aware, original-caseIrPatternand encoding modifiers stay explicit asIrEncodingsteps, so lowering never lowercases, compiles regexes, or expands encodings.rsigma-eval—compile_ruleroutes throughlower_rule→compile_to_compiled, which performs the physical work (lowercasing, regex/aho-corasick, encoding expansion). Public physical API (CompiledRule,evaluate_rule,Engine) is unchanged.rsigma-irHIR cache — all HIR types deriveserde::{Serialize, Deserialize}(and the embeddedrsigma-parsertypes gainedDeserialize), so a lowered rule set round-trips. A newcachemodule serializes a slice ofIrRuleto a versioned, self-describing blob (aHirCacheHeaderwithHIR_SCHEMA_VERSIONplus the rules) viaencode_rules/decode_rules, withto_jsonfor a debug export.decode_rulesversion-checks the header before decoding the rules. CBOR is the binary format because the embeddedLogSource's#[serde(flatten)]map has an unknown length that fixed-layout encoders reject.rsigma-evalrestart cache —Engine::save_hir/Engine::load_hirpersist and reload the engine's lowered rules through the HIR cache, so a warm start skips parse, pipeline, and lowering. The engine retains the post-pipeline HIR for rules added via the parsed-rule paths; filter injections and precompiled rules are not captured, so re-apply filters afterload_hir.rsigma-ir— opt-in, semantics-preserving HIR optimization passes inoptimize:flatten_condition(merge nested same-kind boolean groups, collapseNot(Not), unwrap single-child groups, drop idempotent duplicate siblings),eliminate_dead_detections(prune detections no condition can reference, honoringthem/glob selector patterns and recursing intoConditional), andcommon_subexpressions(a non-mutating report of repeated detection items).optimize_ruleruns the structural passes in order. The passes are not run by the default eval or convert paths, so compiled-matcher behavior and byte-identical backend output are unchanged; a differential test confirms each pass preserves the match decision and the set of matched selections and fields.rsigma-convert— theBackendtrait is IR-native end to end. A rule is lowered toIrRuleonce and both detection and condition walks run over the HIR (convert_rule_via_ir→convert_ir_detection/convert_ir_detection_item). Value leaves take the faithful HIR (convert_field_stroverIrStrOp+ wildcard-awareIrPattern,convert_field_regexwithRegexFlags,convert_field_compare_opwithCompareOp,convert_keyword_str/convert_keyword_num) instead ofSigmaString/Modifier, so a backend never touchesrsigma-parserto emit a value match. PostgreSQL, LynxDB, and Fibratus golden outputs are byte-identical.
Dependency bumps (#354, #357)
Rolls up the open Dependabot PRs into a single merge. Rust (workspace Cargo.lock and fuzz/Cargo.lock): bytes 1.12.0 to 1.12.1 (#322), regex 1.12.4 to 1.13.1 (#326, #319), jsonschema 0.46.9 to 0.48.1 (#325), rmcp 2.1.0 to 2.2.0 (#323) with a co-required sse-stream 0.2.3 to 0.2.4 bump (rmcp 2.2.0 calls the renamed SseStream::from_bytes_stream API), and the patch-updates group (#356) daachorse 3.0.2 to 3.0.3 plus uuid 1.23.4 to 1.23.5. rsigma-parser Cargo.toml pins for yamlpath/yamlpatch move from 1.25 to 1.26 (lockfile already on 1.26.1; yamlpath 1.27.0 is held back until yamlpatch publishes a matching release). CI (all repinned by commit SHA, batched via the actions-updates group, #324, #355): taiki-e/install-action v2.82.8 to v2.83.2, github/codeql-action/upload-sarif v4.36.3 to v4.37.0, and actions/setup-node v4.4.0 to v6.4.0. VS Code extension: typescript 6.0.3 to 7.0.2 (#321) and @types/node 26.1.0 to 26.1.1 (#320). Held back: rusqlite 0.39 to 0.40.1 (#234) on MSRV 1.88.
rstix: serde-gate wire-format validator dependencies (#352)
- Wire MUST at parse (DD-DM-001) — STIX §6.4 / §6.5 / §6.15 require well-formed
domain-name,email-addr, andurlvalues; rstix rejects malformed values at defaultserdeparse rather than accepting them and reporting later. Documented incrates/rstix/README.md. idna,url,email_address,base64,encoding_rs— optional deps enabled by theserdefeature (base64also underpattern);--no-default-featuresbuilds omit them. Defaultserdeparse still enforces DD-DM-001 unchanged.patternimpliesserde— Pattern Engine evaluation usesBundle,StixObject, and typed SCO/SDO types that live behind theserdefeature;--no-default-features --features patternnow compiles. CI adds a lean-feature matrix for--no-default-featuresonrstix.
rstix STIX 2.1 spec-exact closure with Graph + Marking + Store (graph, marking, store features) (#327)
- Wire MUST at parse (Decision A):
domain-name,email-addr, andurlvalues use IDNA / RFC 5322 / RFC 3986 validation at the defaultserdeboundary (idna,email_address,urlare required dependencies). - SCO
*_enc(§3.1 / §3.9.1): IANA charset validation and_enc-without-base pairing on spec-definedfile.name_encanddirectory.path_enc, plus any_enckeys incommon.extra; negative fixtures intests/fixtures/spec/sco/. - Spec-audit closure: observed-data deprecated
objectswith embedded SRO; standalone unknown top-level keys viacommon.extra; granular selector semantics, language-content nested rules, STIX-W0031, and location ISO 3166 / region-ov viaBundle::validate(). - Serialization conventions: wire-facing JSON property bags use
BTreeMap(stable key order for strict round-trip and JCS); internal STIX-id indexes useHashMap— aligned with PR #213 review (ExtensionMap,LanguageContent.contents,common.extra). - Language-content (§7.1.1): recursive object mirroring,
""list placeholders, unknown target fields silently ignored (no advisory). - Granular markings: selector resolution on custom object wire JSON; custom objects validated in the pipeline schema phase.
- Email-message (§6.6): RFC 2047 encoded-word decoding on ingest for header string fields.
- Encryption algorithm: pipeline
property_typescheck aligned to SHOULD warning (W0010), matchingBundle::validate().
Adds three independent modules for Graph + Marking + Store:
Graph (graph feature):
StixGraph::from_bundle— indexes SROrelationshipedges and all typed_ref/_refsproperties (including nested SCO extension refs); duplicate object ids rejected withGraphError::DuplicateObjectId.EdgeTraversal— plan-style chain:from(id).out_edges_matching(pred).targets_as::<T>().RelationshipExpander— multi-hop expansion from any start node (expand_from) or anIndicatorId(expand_from_indicator); collects identity, infrastructure, indicator, malware, threat-actor, campaign, attack-pattern, course-of-action, and vulnerability summaries.SroEdgePayload— typed access to underlyingrelationshiporsightingSRO on graph edges; sighting edges indexed with type"sighting".in_refs/ incoming SRO traversal — bidirectional graph walking on SRO and inlined ref edges.model/ref_paths— path-aware ref inventory shared with bundle ref validation.
Marking (marking feature):
TlpV2Level— all five predefined TLP 2.0 UUIDs;TLP:AMBER+STRICTvsTLP:AMBERwith distinctpermits_disclosure.MarkingResolver—effective_for_object(most restrictive),effective_for_property/effective_for_selector(granular selectors with JSON path resolution),permits_disclosure(audience), andEffectiveMarking::language_tagsfor granular language markings.
Store (store feature):
StixStoretrait (object-safe) +MemoryStorewith versioned SDO/SRO storage, full-text search index,delete, andexport_bundle.FsStore(store-fsfeature) — filesystem-backed durability with JSON object files under a store root.- SCO asserted-id preservation — store key is source id; UUIDv5 fingerprint reported via
FingerprintConflictinImportReport. StixQuerybuilder for typed store queries with pagination (QueryCursor/next_cursor), type-indexed scans,text_search, SCO content updates, andStoreError::InvalidQueryfor out-of-range cursors.
Cloud Schema Signature Bundle (#318)
Built-in schema signatures cover AWS CloudTrail (aws_cloudtrail), AWS VPC Flow Logs (aws_vpcflow), Azure Activity/SignIn/Audit logs (azure_activitylogs, azure_signinlogs, azure_auditlogs), GCP Cloud Audit (gcp_audit), Microsoft 365 unified audit log (m365_audit), GitHub Audit (github_audit), Okta System Log (okta_system_log), OneLogin (onelogin_events), Kubernetes audit (k8s_audit), Docker events (docker_events), and osquery (osquery_result). Each signature uses multi-field markers (high specificity, no misfires) and implies a SigmaHQ taxonomy-compatible product/service logsource for conflict-based pruning. Off-taxonomy sources (k8s, docker, osquery) ship as logsource.custom dimensions to stay within the no-blessed-vocabulary line, and AWS VPC Flow Logs ships as product: aws plus custom: {source: vpcflow}. A gcp_audit.yml pipeline strips the data. prefix that SigmaHQ's gcp.audit rules use (data.protoPayload.*) so they match native Cloud Logging events (protoPayload.*). Per-source golden fixtures (test_event.json + expected_classification.yaml) with a walk test, an end-to-end GCP routing test, and specificity-ordering unit tests guard correctness and prevent regressions. A new cloud collection recipes guide covers Vector, OTel, and Fluent Bit configs per source.