Detections have inputs, outputs, regressions and users who suffer when they break. That makes them software, and the teams who treat them that way ship faster and break less.
The repository is the source of truth
detections/
windows/process_creation/proc_creation_win_encoded_powershell.yml
tests/
proc_creation_win_encoded_powershell/
true_positive_01.json
benign_01.json
pipelines/
sysmon.yml
If a rule can be edited by hand in the SIEM console, it will be, and nobody will know which version is running. Deploy from the main branch and make console edits either impossible or loud.
Metadata is what makes review possible
title: Encoded PowerShell Command
id: 7f4a2c1b-9c2e-4c6a-9f0b-1a2b3c4d5e6f
status: test
description: Detects powershell.exe launched with a base64-encoded command line.
references:
- https://attack.mitre.org/techniques/T1059/001/
author: SOC Detection Team
date: 2026-08-29
tags:
- attack.execution
- attack.t1059.001
falsepositives:
- Backup agent scheduled jobs on the build fleet (filtered)
level: high
id is a UUID you generate once per rule. It is how the SIEM, your tests and your coverage layer keep referring to the same detection across renames. status walks experimental -> test -> stable, and retired rules become deprecated rather than vanishing from history.
Four CI stages that catch real bugs
- Validate.
sigma check detections/catches malformed logsources, duplicate ids and modifier typos before a human reviews anything. - Convert. Run the conversion in CI (
sigma convert -t splunk -p sysmon detections/). A rule using a modifier your backend cannot express should fail the build, not fail quietly at 3 a.m. - Test with events. Every rule ships with at least one true-positive sample and one benign sample drawn from your own estate. The test asserts the rule matches the first and not the second. This is what stops a quick tuning tweak from silently killing a detection.
- Publish coverage. Emit the
attack.tags into an ATT&CK Navigator layer so coverage is a build output instead of a quarterly spreadsheet.
The pull-request checklist
- Is there a true-positive sample, and does it come from a real execution rather than a hand-written guess?
- Are the matched fields robust (behavior, lineage, path) or brittle (one exact string)?
- Is every filter scoped to a specific benign behavior, with the reason recorded in the rule?
- Can an analyst act on it? A rule with no triage guidance is a ticket generator.
- Is it mapped to a technique, and does the estate actually log the fields it needs?
Close the loop
The alert an analyst opens should link back to the rule file that produced it. When they close it as a false positive, that event becomes a benign test sample and a filter in the same pull request. Detection as code is not the YAML - it is the loop from alert to commit and back.
