Lesson 2 of 5 · 25 min read

Detection Repository Layout — Structure That Scales

A detection repository that works for 10 rules often breaks down at 100 rules. Structure it right from the beginning and it scales to 1,000+ without requiring a reorganization. This lesson covers the layout used by mature detection programs and explains why each directory exists.


The Reference Layout

detection-rules/
├── rules/
│   ├── windows/
│   │   ├── process_creation/
│   │   │   ├── proc_creation_powershell_encoded_cmd.yml
│   │   │   ├── proc_creation_lolbin_regsvr32.yml
│   │   │   └── proc_creation_whoami_recon.yml
│   │   ├── registry_event/
│   │   │   └── registry_run_key_persistence.yml
│   │   ├── file_event/
│   │   │   └── file_creation_lsass_dump.yml
│   │   └── process_access/
│   │       └── proc_access_lsass_memory.yml
│   ├── linux/
│   │   ├── process_creation/
│   │   └── auditd/
│   └── cloud/
│       ├── aws/
│       │   └── cloudtrail/
│       └── azure/
│           └── activity_log/

├── tests/
│   └── windows/
│       └── process_creation/
│           ├── proc_creation_powershell_encoded_cmd-tp.json
│           └── proc_creation_powershell_encoded_cmd-fp.json

├── pipelines/
│   ├── elastic-ecs.yml          ← official ECS pipeline (from pySigma)
│   ├── splunk-sysmon.yml        ← Sysmon field names for Splunk
│   ├── sentinel-asim.yml        ← Microsoft Sentinel ASIM
│   └── custom-splunk-env.yml    ← your org's custom field names

├── docs/
│   └── windows/
│       └── proc_creation_powershell_encoded_cmd-ADS.md

├── .github/
│   └── workflows/
│       ├── pr-checks.yml        ← runs on every PR
│       └── deploy.yml           ← runs on merge to main

├── scripts/
│   ├── convert_all.sh           ← bulk sigma convert helper
│   └── validate_ids.py          ← check no duplicate rule IDs

└── README.md

The rules/ Directory

Organization principle: Match Sigma’s logsource category hierarchy.

rules/{product}/{category}/rule-name.yml

This directly maps to how sigma-cli routes rules:

Naming convention:

{scope}_{subject}_{specific-behavior}.yml

proc_creation_powershell_encoded_cmd.yml
proc_access_lsass_memory.yml
registry_run_key_persistence.yml
network_connection_powershell_outbound.yml

Long, descriptive names — don’t abbreviate. The file name should tell another engineer what the rule detects without opening it.


The tests/ Directory

Each rule should have at least one TP fixture:

// tests/windows/process_creation/proc_creation_powershell_encoded_cmd-tp.json
[
  {
    "EventID": 1,
    "Image": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
    "CommandLine": "powershell.exe -EncodedCommand SGVsbG8gV29ybGQ=",
    "ParentImage": "C:\\Windows\\explorer.exe",
    "User": "CORP\\testuser",
    "ComputerName": "WIN10-TEST",
    "UtcTime": "2026-05-20 10:15:32.000"
  }
]

And a FP fixture:

// tests/windows/process_creation/proc_creation_powershell_encoded_cmd-fp.json
[
  {
    "EventID": 1,
    "Image": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
    "CommandLine": "powershell.exe -Command \"Write-Host 'hello'\"",
    "ParentImage": "C:\\Windows\\explorer.exe",
    "User": "CORP\\testuser",
    "ComputerName": "WIN10-TEST",
    "UtcTime": "2026-05-20 10:20:00.000"
  },
  {
    "EventID": 1,
    "Image": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
    "CommandLine": "powershell.exe -NonInteractive -ExecutionPolicy Bypass -File update.ps1",
    "ParentImage": "C:\\Windows\\CCM\\CcmExec.exe",
    "User": "CORP\\testuser",
    "ComputerName": "WIN10-TEST",
    "UtcTime": "2026-05-20 10:25:00.000"
  }
]

The CI pipeline runs the converted query against both fixtures and asserts:


The pipelines/ Directory

Sigma’s generic field names must be mapped to your SIEM’s actual field names. The pipeline file does this translation:

# pipelines/custom-splunk-env.yml
name: Custom Splunk Environment
priority: 10
transformations:
  # Our Sysmon data uses different field names than standard CIM
  - id: sysmon_field_mapping
    type: field_name_mapping
    mapping:
      CommandLine: process_command_line    # our custom field name
      Image: process_image
      ParentImage: parent_process_image
      User: user_name
    rule_conditions:
      - type: logsource
        product: windows
        category: process_creation

  # Our events land in a specific index
  - id: sysmon_index
    type: detection_item_keyword
    keyword: "index=corp_sysmon"
    rule_conditions:
      - type: logsource
        product: windows

This pipeline is the most environment-specific file in the repo — it encodes your organization’s actual SIEM configuration. Maintaining it carefully is what makes automated conversion produce working queries.


The docs/ Directory

Each rule has a corresponding ADS document:

docs/windows/proc_creation_powershell_encoded_cmd-ADS.md

The ADS is created as part of the same PR that introduces the rule. Rule + ADS merge together — they’re a unit. The ADS template covers: Goal, Categorization, Strategy Abstract, Technical Context, Blind Spots, False Positives, Validation, Priority, and Response Actions.


Real-World Repository Patterns

Elastic detection-rules

detection-rules/
├── rules/                          ← TOML format (not Sigma)
│   └── windows/
│       └── credential_access_lsass_memdump.toml
├── tests/
│   └── data/
│       └── unittest/
├── detection_rules/                ← Python package for rule management
│   ├── cli.py                      ← CLI: python -m detection_rules ...
│   └── rule_validation.py
└── .github/workflows/
    └── test.yml

Uses TOML instead of Sigma — Elastic-specific format that maps directly to Kibana Detection Engine rules. Better for Elastic-only teams; sacrifices portability.

panther-analysis

panther-analysis/
├── aws_cloudtrail_rules/
│   ├── aws_root_activity.py         ← Rules written in Python functions
│   └── aws_root_activity_test.py    ← pytest-based unit tests
├── gcp_audit_rules/
└── policies/

Panther uses Python for rule logic instead of Sigma YAML. Enables arbitrary logic complexity; sacrifices cross-SIEM portability. Tests are standard pytest — any Python developer can write them.

SigmaHQ (community standard)

sigma/
├── rules/
│   └── windows/
│       └── process_creation/
│           └── proc_creation_win_powershell_encoded_cmd.yml
├── tests/
└── tools/                          ← Legacy; sigma-cli is the modern tool

The reference implementation. All rules in Sigma YAML. CI validates schema, checks for duplicate IDs, and runs sigma-cli conversion to verify all backends work.