Skip to content

Commit 7326aef

Browse files
authored
Merge pull request #286 from timescale/feat/rule-draft
feat: rule drafting from logs (`rsigma rule draft`)
2 parents 378f750 + 7230069 commit 7326aef

16 files changed

Lines changed: 3198 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to RSigma are documented in this file. Each entry correspond
44

55
## [Unreleased]
66

7+
### Rule drafting from logs (#286)
8+
9+
Turns exemplar events into a complete draft Sigma rule, the detection-authoring sibling of schema signature discovery: feed the malicious or noteworthy events (optionally contrasted against a baseline corpus of normal traffic) and get back paste-ready standard Sigma YAML to review, edit, and commit. The tool proposes, a human decides; metadata stays as explicit `TODO` placeholders.
10+
11+
* **Drafting core** — a new `rsigma_eval::rule_draft` module profiles every field across the exemplars, drops volatile fields (timestamp-shaped names and values, UUID/GUID shapes, per-event counters, high-entropy unique values), scores the survivors by value stability times baseline rarity, infers a value form and modifier per field (plain equals, OR value list, `endswith`/`startswith` from a shared path tail/prefix, `contains`/`contains|all` from shared tokens with a minimum token length and baseline-generic rejection), escapes literal Sigma wildcards in observed values, splits exemplar variants into `selection_*` groups with `1 of selection_*` when the split is earned, and infers the logsource from the built-in schema classifier (a shared Sysmon EventID maps to its Sigma category). The core is pure and deterministic: the rule `id` is caller-supplied and repeated runs are byte-identical.
12+
* **Verified before emitted** — the draft is parsed and compiled through the real evaluation engine and must match every exemplar (fields that break the match are dropped, bounded by a minimum-field floor; below it the command errors instead of emitting an over-broad rule), the lint catalogue runs over the YAML with findings surfaced as warnings, and the baseline hit count and rate are reported as the estimated false-positive rate.
13+
* **`rule draft`** — the offline command: exemplars via inline JSON, `@file` NDJSON, `@file.evtx` (with the `evtx` feature), or stdin, plus `--baseline @file`. Flags: `--max-fields`, `--min-prevalence`, `--include-field`/`--exclude-field`, `--logsource-category`/`--logsource-product`/`--logsource-service`, `--title`, `--skip-baseline-eval`, and `--emit yaml|report` (default `yaml` prints the rule with the field report on stderr; `report` renders the full analysis through the global output formats). The UUIDv4 `id` is generated at the CLI layer.
14+
* **Docs** — a new `rule draft` CLI page and a Drafting Rules from Logs guide, including the schema-native note: the draft uses the exemplars' native field names, so evaluate it without a mapping pipeline.
15+
716
### Schema signature discovery (#285)
817

918
Turns the unknown-schema signal the schema tooling surfaces into ranked candidate declarative signatures, so operators stop hand-writing every signature from scratch. Pure-Rust, glass-box mining (clustering plus discriminative feature selection); the output is the same `schemas:` YAML the classifier already consumes, so every proposed predicate is human-readable and reviewable. Additive and opt-in throughout; no black-box model, and nothing is applied automatically.

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ rsigma rule fields -r rules/
348348
# Show fields after pipeline mapping
349349
rsigma rule fields -r rules/ -p ecs.yml --json
350350

351+
# Draft a detection rule from exemplar events, contrasted against a baseline corpus
352+
rsigma rule draft -e @incident.ndjson --baseline @normal-day.ndjson
353+
351354
# Backtest a corpus against per-rule expectations (CI fixture harness)
352355
rsigma rule backtest -r rules/ --corpus ci/corpus/ --expectations ci/expectations.yml
353356

crates/rsigma-cli/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] }
5959
chrono = { version = "0.4", default-features = false, features = ["std", "now"] }
6060
humantime = "2"
6161
anyhow = "1"
62+
# Rule drafting: the deterministic core in rsigma-eval takes a caller-supplied
63+
# id; the CLI generates the UUIDv4 (already in the tree via rsigma-runtime).
64+
uuid = { version = "1", features = ["v4"] }
6265
arc-swap = "1"
6366
similar = "2.7.0"
6467

0 commit comments

Comments
 (0)