Honest Sigma-rule backtest harness
Project description
sigmaforge
Honest Sigma-rule backtest harness. Measures detection rules against real log
corpora and reports two numbers per rule — recall (does it catch attacks of its
ATT&CK sub-technique?) and precision / false-positives (does it fire on benign
activity?) — with honesty gates that return unmeasured instead of a fake 0 or a
tautological 1.0 when the data can't support a number.
[!NOTE] Learning / portfolio project, built by directing AI coding agents. Christian Huhn (photography → SOC career change) designed, reviewed, and gated the work; the implementation was AI-pair-programmed. It is an honest measurement harness, not a polished product — see Status below for exactly what works and what doesn't yet.
What problem it solves
Every SOC ships dozens to hundreds of detection rules and rarely measures them. sigmaforge answers two questions with reproducible evidence:
- Which rules are noise generators? (high false-positives on legitimate activity)
- Which rules catch nothing? (zero recall against real attacks of their technique)
Example finding from a real run: Suspicious Windows Service Tampering produced 66 false-positives on a benign corpus — every one a Ninite / TeamViewer installer, not an attack.
How it actually works
flowchart LR
R[SigmaHQ rules] -->|partition high/critical| C[compile to one Zircolite ruleset]
C --> E[Zircolite engine]
A[(attack corpus<br/>sub-technique-labeled)] --> E
B[(benign corpus<br/>Nextron + OpTC)] --> E
E --> S[score: recall per technique<br/>+ precision/FP label-aware]
S --> G[honesty gates<br/>floor · positive-control · no-self-review]
G --> O[report.md + manifest]
The real pipeline is script-driven (scripts/run6_backtest.py is the current
end-to-end path):
uv run python scripts/compile_loaded_ruleset.py # rules -> one Zircolite ruleset
uv run python scripts/run6_backtest.py # backtest -> reports/run6.md
[!WARNING] The shipped CLI (
sigmaforge backtest) is a weaker, work-in-progress path and is not the way the real reports were produced. Use the scripts above. The CLI is kept for the future one-command experience, not parity.
Status
| Area | State |
|---|---|
| Recall (per sub-technique, no sibling dilution) | Working — 338/609 rules measurable, 70 fire (run5) |
| Precision / false-positives (label-aware, gated) | Working — 7/609 measurable on current benign corpus (run6) |
| Honesty gates (floor, positive-control, no-self-review) | Working |
| Reproducible manifest (run_hash, corpus SHAs, provenance) | Working |
One-command CLI (sigmaforge backtest) |
WIP — weaker than the scripts |
| Self-generated benign corpus | Kit ready (scripts/selfgen/), needs a Windows VM run |
[!IMPORTANT] The log corpora are not shipped. They are large, separately licensed, and gitignored.
pip install sigmaforgeinstalls the harness code, not the data — a full end-to-end backtest needs the corpora and a local Zircolite checkout (also not bundled). The package is useful as a library / reference; the runnable pipeline needs the local setup documented inscripts/.
Install
pip install sigmaforge
Installs the harness package and the sigmaforge CLI. The detection engine
(Zircolite) and the log corpora are obtained
separately (see above).
Corpora used (all verified, portfolio-safe licenses)
| Corpus | Role | License |
|---|---|---|
| splunk/attack_data | recall (sub-technique-labeled attacks) | Apache-2.0 |
| DARPA OpTC | precision (real enterprise benign week) | Public domain |
| NextronSystems/evtx-baseline | precision (goodware baseline) | Apache-2.0 |
Self-generated (scripts/selfgen/) |
precision (targeted admin/LOLBin noise) | your own lab |
Reproduce a backtest from a clone
The pip package is the library/CLI; a full end-to-end backtest needs the engine and corpora, which are not bundled (engine = large third-party tool; corpora = large and separately licensed). To go from a fresh clone to a runnable backtest:
uv sync --group backtest # script deps (pyyaml, evtx, gdown)
bash scripts/setup_engine.sh # fetch Zircolite 3.7.6 + its runtime deps
Then obtain the corpora (see the table above) into ~/sigmaforge-v0/ and build the
combined samples with the scripts/build_*_benign.py / scripts/build_*_corpus.py
helpers, compile the ruleset, and run a backtest:
uv run python scripts/compile_loaded_ruleset.py # SigmaHQ rules -> one Zircolite ruleset
uv run python scripts/run7_backtest.py # -> reports/run7.md (+ manifest)
Run scripts from the repo root (the engine working dir defaults to the current
directory; override with SIGMAFORGE_HOME if you keep Zircolite/ elsewhere). The
reports/run*.md + *_manifest.json are committed so the results are inspectable
without re-running.
Development
Built with the Shipwright dev framework.
uv sync --dev
uv run pytest # 110 tests (engine smoke test skips without Zircolite)
uv run ruff check .
License
MIT © Christian Huhn. Corpus data retains its upstream license (see table above).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sigmaforge-0.2.0.tar.gz.
File metadata
- Download URL: sigmaforge-0.2.0.tar.gz
- Upload date:
- Size: 38.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e014ae35d91243fe322b9a59ccbf23525a16fd56acd525d2fd1860615f5ad7eb
|
|
| MD5 |
f536fc97c18e37d6a918693910aa9430
|
|
| BLAKE2b-256 |
ca7e52f256db6d6f3aa9ad60d02cc9f6d40e1999ec1bbe2dbb8101dffc7284ad
|
Provenance
The following attestation bundles were made for sigmaforge-0.2.0.tar.gz:
Publisher:
release.yml on duathron/sigmaforge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sigmaforge-0.2.0.tar.gz -
Subject digest:
e014ae35d91243fe322b9a59ccbf23525a16fd56acd525d2fd1860615f5ad7eb - Sigstore transparency entry: 1862398490
- Sigstore integration time:
-
Permalink:
duathron/sigmaforge@4f90fa1984bbc40faaae405f77455544c945ed61 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/duathron
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4f90fa1984bbc40faaae405f77455544c945ed61 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sigmaforge-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sigmaforge-0.2.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
934040d910d1e8ef5a478b8e6380071cb653a5ff6e4f01b71652358265a143fa
|
|
| MD5 |
e0c91dfad2622bbff7891634dc1a0b96
|
|
| BLAKE2b-256 |
6897e8d6605a2c5a8c224facb4f65d10360ff2fe7b1c10791b1c29fef6570c5c
|
Provenance
The following attestation bundles were made for sigmaforge-0.2.0-py3-none-any.whl:
Publisher:
release.yml on duathron/sigmaforge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sigmaforge-0.2.0-py3-none-any.whl -
Subject digest:
934040d910d1e8ef5a478b8e6380071cb653a5ff6e4f01b71652358265a143fa - Sigstore transparency entry: 1862398654
- Sigstore integration time:
-
Permalink:
duathron/sigmaforge@4f90fa1984bbc40faaae405f77455544c945ed61 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/duathron
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4f90fa1984bbc40faaae405f77455544c945ed61 -
Trigger Event:
push
-
Statement type: