Detection-rule generation Turn an IR finding into a portable Sigma rule that any SIEM can consume. Closes the gap between "we noticed something here" and "every host runs a detection for it."

Why this exists

Findings answer "what happened on this host?" Detection rules answer "what should fire on any host when this happens again?" The hand-off between investigation and detection engineering is, in most orgs, a human Slack message. digger automates it: a finding's evidence carries enough structure to write the corresponding Sigma rule mechanically, with a stable UUID, ATT&CK tags, and a reference back to the original case for provenance.

Two paths: per-finding vs per-detector

digger emits Sigma in two distinct flows:

Run it

# Per-finding mode
digger generate sigma --case-dir ./case-1
# generated 4 Sigma rules from 7 findings
# out: ./case-1/sigma-out

# Just one finding by UUID
digger generate sigma --case-dir ./case-1 --finding ae6b2cac-b996-4a29-bc2e-15e86d4f5412

# Custom output directory
digger generate sigma --case-dir ./case-1 --out-dir ./sigma-rules/

# Per-detector templates (no case needed)
digger generate sigma --from-detectors --out-dir out/sigma/
# generated 13 per-detector Sigma templates
# out: out/sigma

# Verbose: print every written filename
digger generate sigma --case-dir ./case-1 -v

Each rule lands in its own .yml file named <uuid>_<title>.yml. The UUIDs are deterministic (SHA-256 of the finding content), so re-running on the same case overwrites the same files rather than producing duplicates.

What gets mapped

Finding detectorSigma log sourceWhat gets emitted
lolbinsprocess_creationImage ends with the LOLBin name, CommandLine contains the distinguishing fragment
suspicious_processesprocess_creationImage + ParentImage + CommandLine selectors
c2 (cmdline pattern)process_creationCommandLine regex from the matched framework pattern
c2 (IP)network_connectionDestinationIp selector
c2 (URL)proxyc-uri contains the matched URL fragment
shai_hulud (package)file_eventTargetFilename ends with /node_modules/<pkg>/package.json + version filter
shai_hulud (workflow)file_eventTargetFilename ends with .github/workflows/shai-hulud-workflow.yml
env_hijackprocess_creationEnvironmentVariables contains the hijack variable=value
persistence_outlierfile_eventTargetFilename contains scratch/user-path fragments
threat_actorprocess_creationCommandLine regex from the attributed TTP pattern
reconlinux auth / network_connectionSSH brute-force / banner-grab pattern OR count-aggregation of distinct DestinationPort per SourceIp
exploitation (service→shell)process_creationParentImage ends with /nginx, /httpd, /php-fpm, etc. AND Image ends with /sh, /bash, /cmd.exe, etc.
exploitation (RCE chain)process_creationImage ends with shell name
exploitation (shellcode cmdline)process_creationCommandLine contains all of the pattern's discriminating tokens
exploitation (weblog)webservercs-uri-query / cs-uri-stem contains the exploit signature (jndi: / classLoader / etc.)
privescfile_event / process_creationTargetFilename + FileMode for setuid; Image=/setcap or /insmod for capability / kernel-module signals
lateralnetwork_connection / process_creation / windows.securityPer-kind: outbound RFC1918, credential-dumper cmdline, lateral toolkit Image, SSH ProxyJump, or 4624 + NTLM + anonymous workstation (PtH)
ad_attackswindows.security / process_creationPer-kind: 4769 RC4-HMAC, 4768 PreAuthType 0, 4662 replication-rights GUID, 5136 AdminSDHolder, BloodHound family Image-ends-with
cloud_attacksnetwork_connection / process_creation / file_eventPer-kind: 169.254.169.254 DestinationIp, container-escape cmdline, kubeconfig file_event with kube-client filter, cloud-CLI cmdline
counter_reprocess_creationImage ends with /gdb, /lldb, /x64dbg.exe, etc. AND CommandLine contains target-PID
persistent_sessionsprocess_creation / file_eventPer-kind: tmux/screen/zellij parented by network-service Image; user-systemd file_event; nohup/setsid Image
attacker_toolingprocess_creationImage ends with the specific tool binary name

Findings whose evidence doesn't map cleanly to a Sigma log source (memory_anomaly, unsigned_binary, browser extension permissions, etc.) are silently skipped — the generator returns None and the CLI reports the count delta.

Per-detector templates (to_sigma_template())

The base Detector class exposes a to_sigma_template() classmethod. A detector that implements it declares a generic SIEM rule for the class of behavior it watches — independent of any specific case finding. The --from-detectors CLI flag iterates every registered detector and writes one rule per implementer.

Every Decepticon-countermeasure detector ships a template; the test tests/test_genrule_detector_templates.py round-trips each generated rule through SigmaLoader to enforce validity.

Sample output

title: 'Shai-Hulud compromised npm package: chalk@5.6.1'
description: 'package on the worm list.
  Auto-generated from digger finding ae6b2cac-b996-4a29-bc2e-15e86d4f5412.'
id: 42ca0822-82c9-4ed2-a356-a8b4c051d4fa
status: experimental
author: digger
date: 2026/05/21
modified: 2026/05/21
references:
- digger-case://sigma-demo/ae6b2cac-b996-4a29-bc2e-15e86d4f5412
level: critical
tags:
- attack.t1195.002
- attack.initial_access
- attack.supply_chain_compromise
falsepositives:
- Investigate context before alerting on this rule.
logsource:
  category: file_event
detection:
  selection:
    TargetFilename|endswith: /node_modules/chalk/package.json
    TargetFilename|contains: /node_modules/
  filter_version:
    TargetFilename|contains: '"version": "5.6.1"'
  condition: selection AND filter_version

From digger to your SIEM

The generated rules are standard SigmaHQ format. Feed them through sigma-cli / pySigma to compile to your SIEM's query language:

# Splunk
sigma convert -t splunk -p sysmon ./case-1/sigma-out/*.yml

# Elastic
sigma convert -t lucene -p ecs_windows ./case-1/sigma-out/*.yml

# Microsoft Sentinel (KQL)
sigma convert -t microsoft_sentinel ./case-1/sigma-out/*.yml

# Chronicle (YARA-L)
sigma convert -t chronicle ./case-1/sigma-out/*.yml

The Sigma references field carries digger-case://<case_id>/<finding_uuid> so the provenance survives every back-end conversion — if a deployed rule fires in production, an analyst can trace it back to the original investigation.

Detection-engineering workflow

# 1. Investigation produces findings on host-of-interest
digger investigate --case-dir ./case-2026-05-21

# 2. Auto-generate Sigma from the findings
digger generate sigma --case-dir ./case-2026-05-21

# 3. Review (pruning over-broad rules, tightening selectors)
# 4. Commit to your detection-rules repo
# 5. CI compiles for your SIEM and deploys
# 6. The same kind of activity now fires on every host in scope

Limits