SigmaWarden
A linter for Sigma detection rules. Points
sigmawarden lint at a folder and it flags the mistakes that don't show up
until someone actually tries to deploy the rule — a logsource that doesn't
match anything real, a condition referencing a selection you renamed and
forgot to update, a falsepositives field nobody filled in, an ATT&CK
technique tag with a digit transposed.
I built this after the third time I shipped a rule to my own
detections page with an empty
falsepositives list, because I'd written the detection logic, felt done, and
closed the file. None of those mistakes break the YAML. They just mean the
rule looks finished and isn't.
Install
pip install sigmawarden
Or from source:
git clone https://github.com/zephryxsec/SigmaWarden
cd SigmaWarden
pip install -e .
Python 3.9+, one runtime dependency (PyYAML). No network calls at lint time — the ATT&CK technique list and the logsource reference are both bundled.
Usage
sigmawarden lint ./rules
rules/dcsync.yml
warning SW011 missing 'falsepositives'
rules/kerberoasting.yml
error SW010 condition 'selction and not 1 of filter_*' references 'selction', which isn't defined in 'detection'
rules/golden-ticket.yml
warning SW016 tag 'attack.t1003.099' — T1003.099 isn't a current ATT&CK technique ID (typo, or it's been deprecated/revoked since this reference was last updated)
3 rule(s) across 3 file(s) — 1 error(s), 2 warning(s), 0 info
Exit code is 1 if anything at error severity turned up, 0 otherwise.
Point it at a single file instead of a directory if that's all you want
checked.
sigmawarden lint ./rules --strict # also fail on warnings
sigmawarden lint ./rules --format json # for CI / other tooling to consume
sigmawarden lint ./rules --ignore SW011,SW016 # skip specific checks
sigmawarden lint ./rules --min-severity error # only show what would fail --strict... plus this
sigmawarden new-id # print a fresh UUID for a rule's `id:` field
Custom taxonomy — internal log sources, a vendor product not in the bundled
reference — won't trip false positives forever. Point --extra-logsource at
a JSON file shaped like sigmawarden/data/logsource_reference.json (only the
keys you need) and it's merged in.
Checks
| Code | Severity | What it means |
|---|---|---|
| SW001 | error | missing title |
| SW002 | error | missing id |
| SW003 | warning | id isn't a UUID |
| SW004 | error | logsource missing, or none of product/category/service set |
| SW005 | warning | logsource.product isn't recognized |
| SW006 | warning | logsource.category isn't recognized |
| SW007 | info | logsource.service isn't recognized for that product |
| SW008 | error | detection missing or empty |
| SW009 | error | detection has no condition |
| SW010 | error | condition references a selection that isn't defined |
| SW011 | warning | falsepositives missing or empty |
| SW012 | info | falsepositives is just Unknown |
| SW013 | warning | level isn't one of Sigma's five values |
| SW014 | warning | status isn't one of Sigma's five values |
| SW015 | error | an attack.tXXXX tag is malformed |
| SW016 | warning | an attack.tXXXX tag isn't a known technique ID |
| SW017 | error | two rules in the scanned set share an id |
| SW018 | error | a correlation rule's correlation.rules is missing or empty |
Correlation-type rules (a correlation: block instead of detection:) are
recognized and skip the logsource/detection checks — they're a different
Sigma object, not a malformed regular rule. Aggregation expressions after a
| in a condition (selection | count() by field > 5) are parsed as what
they are, not as undefined selections.
CI
- name: Lint Sigma rules
run: |
pip install sigmawarden
sigmawarden lint ./rules --strict
On the bundled reference data
sigmawarden/data/attack_techniques.json is extracted from MITRE's
attack-stix-data
(Enterprise ATT&CK, revoked/deprecated techniques excluded) — regenerate it
with python scripts/update_attack_data.py when a new ATT&CK release adds or
retires techniques. sigmawarden/data/logsource_reference.json is curated
from the SigmaHQ rule corpus, not a formal spec — logsource is an
intentionally open taxonomy, so SW005–SW007 are warnings, not errors, and
--extra-logsource exists for exactly this reason.
Contributing
Issues and PRs welcome. If you're adding a check, keep it in checks.py as
an independent function that yields Findings — no shared state with the
others — and add a fixture under tests/fixtures/ that would trip it.
License
MIT — see LICENSE.
Metadata
Release files for sigmawarden 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sigmawarden-0.1.0.tar.gz | 24.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sigmawarden-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.2 kB
Release files / sigmawarden-0.1.0.tar.gz
| Download URL | sigmawarden-0.1.0.tar.gz |
|---|---|
| Size | 24.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f1d59fbc02dd9f33da66a56d4ae3db8a0ff231b545aa9a30b9c418a27f725e0
|
|
BLAKE2b-256 checksum How to use checksums |
722c05351aba8f5a86a3cf76b0012a35f9e2176f4cd018ef9aa72425a1b4e4cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency logRelease files / sigmawarden-0.1.0-py3-none-any.whl
| Download URL | sigmawarden-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1908de02f4412e3bb2fc343bd8ddf8e25e210795a0634b9f9eb477b74385215a
|
|
BLAKE2b-256 checksum How to use checksums |
0e9de91527ae42925e98123ecd579ecbfe57b23d5a334c1c4654b38b0f83a02f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency log