Detection-as-Code-Pipeline mit Git und CI/CD
Im Jahr 2025 werden nur noch die 35 % der Sicherheitsteams verwendet Detection-as-Code systematisch, trotz 63 % der Fachleute Sie erklären, dass Sie es regelmäßig anwenden möchten. Diese Kluft zwischen Absicht und Praxis offenbart ein echtes Problem: Die meisten Organisationen wissen es nicht als Erstellen Sie eine robuste DaC-Pipeline, die den gesamten Zyklus abwickelt Lebensdauer einer Erkennungsregel, von der Erstellung bis zur Bereitstellung in der Produktion.
Erkennung als Code (DaC) wendet Ingenieurspraktiken an von Software in den Sicherheitsbereich: Versionskontrolle, Codeüberprüfung, Tests automatisiert, CI/CD-Pipeline und kontrollierte Bereitstellung. Das Ergebnis ist eins drastische Verkürzung der Zeit zwischen der Entdeckung einer neuen Angriffstechnik und der Einsatz einer wirksamen Erkennung mit Qualitätsregeln messbar und im Laufe der Zeit verbesserungsfähig.
Was Sie in Diesem Artikel Lernen Werden
- Struktur eines DaC-Repositorys: Verzeichnislayout, Namenskonvention, Schema
- Workflow Git: branching strategy, commit convention, pull request review
- GitHub Actions pipeline: lint, validate, test, convert, deploy
- Automatisierte Tests mit synthetischen Protokollen: positiv und negativ
- Deployment sicuro a Splunk, Elastic e Sentinel via API
- Automatisches Rollback und Vorfallmanagement nach der Bereitstellung
- Qualitätsmetriken: Abdeckung, Falsch-Positiv-Rate, Bereitstellungshäufigkeit
- Integration mit JIRA/GitHub Issues zur Erkennungsverfolgung
weil die Erkennung als Code von grundlegender Bedeutung ist
Vor DaC war der typische Prozess zum Aktualisieren einer Regel in einem SIEM: Greifen Sie auf die Webkonsole zu, suchen Sie die Regel, bearbeiten Sie sie manuell, speichern Sie sie, hoffe es geht nichts kaputt. Keine Vorgängerversionen, keine Tests, nein Peer-Review, keine Nachvollziehbarkeit von Änderungen. Als eine Regel begann Um 3 Uhr morgens Tausende von Fehlalarmen zu generieren, war unmöglich zu verstehen wer was wann getan hat.
DaC verwandelt diesen chaotischen Prozess in etwas Systematisches:
- Jede Regel ist eine in Git versionierte Textdatei
- Jede Änderung durchläuft einen Pull-Request mit obligatorischer Überprüfung
- Vor der Zusammenführung führt die Pipeline automatische Tests für synthetische Protokolle durch
- Die Bereitstellung erfolgt nur, wenn alle Tests erfolgreich sind
- Bei Problemen ist ein Rollback eine einfache Sache
git revert
Struktur des DaC-Repositorys
Ein gut strukturiertes DaC-Repository ist die Grundlage, auf der alles andere aufbaut. Hier ist das empfohlene Layout für eine Unternehmensorganisation:
detection-as-code/
├── .github/
│ ├── workflows/
│ │ ├── pr-validation.yml # Validazione su ogni PR
│ │ ├── main-deploy.yml # Deploy su merge in main
│ │ └── nightly-test.yml # Test notturno su staging
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── CODEOWNERS
├── rules/
│ ├── windows/
│ │ ├── credential_access/
│ │ ├── lateral_movement/
│ │ └── persistence/
│ ├── linux/
│ ├── cloud/
│ │ ├── aws/
│ │ ├── azure/
│ │ └── gcp/
│ └── network/
├── tests/
│ ├── fixtures/
│ │ └── windows/
│ │ └── credential_access/
│ │ └── cred_mimikatz_lsass/
│ │ ├── positive/
│ │ └── negative/
│ └── unit/
│ └── test_sigma_rules.py
├── pipelines/
│ ├── splunk_enterprise.yml
│ └── elastic_ecs.yml
├── scripts/
│ ├── validate.py
│ ├── test_runner.py
│ ├── convert.py
│ └── deploy_splunk.py
└── Makefile
Namenskonvention und Regelschema
Eine kohärente und wesentliche Namenskonvention für die Navigationsfähigkeit des Repositorys mit Hunderten von Regeln. Die empfohlene Dateinamenkonvention:
# Pattern: <tactic_prefix>_<technique_name>_<context>.yml
cred_mimikatz_lsass.yml # credential_access
lat_psexec_remote_execution.yml # lateral_movement
per_scheduled_task_creation.yml # persistence
def_timestomp_modification.yml # defense_evasion
exe_powershell_encoded.yml # execution
exf_dns_tunneling.yml # exfiltration
c2_http_beaconing.yml # command_and_control
Verzweigungsstrategie für DaC
# Flusso standard per nuove detection
git checkout -b detection/T1003-lsass-dump-via-procdump
# ... crea/modifica la regola e i test ...
git add rules/windows/credential_access/cred_lsass_procdump.yml
git add tests/fixtures/windows/process_creation/procdump_positive.json
git commit -m "feat(detection): T1003.001 LSASS dump via ProcDump
Adds detection for LSASS memory dump via legitimate ProcDump utility.
ATT&CK: T1003.001 - OS Credential Dumping: LSASS Memory
Severity: high
Test coverage: 3 positive, 2 negative fixtures"
git push origin detection/T1003-lsass-dump-via-procdump
# Flusso hotfix per detection urgenti (attacco in corso)
git checkout -b hotfix/active-incident-lateral-movement-mar2026
git commit -m "hotfix(detection): emergency rule for active APT lateral movement
Active incident response. Incident: INC-2026-0312"
Komplette CI/CD-Pipeline mit GitHub-Aktionen
# .github/workflows/pr-validation.yml
name: Detection Rule Validation
on:
pull_request:
paths:
- 'rules/**/*.yml'
- 'tests/**'
jobs:
lint-yaml:
name: YAML Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run yamllint
uses: ibiqlik/action-yamllint@v3
with:
file_or_dir: rules/
config_data: |
extends: default
rules:
line-length:
max: 120
validate-schema:
name: Schema Validation
runs-on: ubuntu-latest
needs: lint-yaml
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install pyyaml jsonschema sigma-cli
- name: Validate Sigma schemas
run: |
python scripts/validate.py ./rules \
--schema schemas/sigma_rule_schema.json
- name: Check UUID uniqueness
run: python scripts/check_uuid_uniqueness.py ./rules
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
needs: validate-schema
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install pytest pyyaml sigma-cli pySigma-backend-splunk
- name: Run detection tests
run: |
pytest tests/unit/ \
-v \
--tb=short \
--junit-xml=test-results.xml
convert-validate:
name: Conversion Test
runs-on: ubuntu-latest
needs: unit-tests
strategy:
matrix:
target: [splunk, elasticsearch, microsoft365defender]
steps:
- uses: actions/checkout@v4
- run: |
pip install sigma-cli \
pySigma-backend-splunk \
pySigma-backend-elasticsearch \
pySigma-backend-microsoft365defender
- name: Test conversion to ${{ matrix.target }}
run: |
sigma convert -t ${{ matrix.target }} rules/ \
2>&1 | tee conversion-${{ matrix.target }}.log
# .github/workflows/main-deploy.yml
name: Deploy Detection Rules to Production
on:
push:
branches: [main]
paths: ['rules/**/*.yml']
jobs:
deploy-splunk:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Identify changed rules
id: changed-rules
run: |
CHANGED=$(git diff --name-only HEAD~1 HEAD -- 'rules/**/*.yml')
echo "files=$CHANGED" >> $GITHUB_OUTPUT
- name: Convert changed rules
run: |
pip install sigma-cli pySigma-backend-splunk
echo "${{ steps.changed-rules.outputs.files }}" | while read rule; do
[ -f "$rule" ] && sigma convert -t splunk -p splunk_windows "$rule" \
>> converted.spl
done
- name: Deploy to Splunk via REST API
env:
SPLUNK_HOST: ${{ secrets.SPLUNK_HOST }}
SPLUNK_TOKEN: ${{ secrets.SPLUNK_TOKEN }}
run: |
python scripts/deploy_splunk.py \
--rules-file converted.spl \
--host "$SPLUNK_HOST" \
--token "$SPLUNK_TOKEN" \
--dry-run false
- name: Notify on failure
if: failure()
uses: actions/github-script@v7
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'DEPLOY FAILURE: Detection rules deployment failed',
body: 'Run: ' + context.serverUrl + '/' +
context.repo.owner + '/' + context.repo.repo +
'/actions/runs/' + context.runId,
labels: ['incident', 'deployment-failure']
})
Testrahmen für Erkennungsregeln
Das Testen ist das Herzstück von DaC. Hier ist das vollständige Python-Test-Framework:
#!/usr/bin/env python3
"""Detection Rule Test Framework."""
import json
import yaml
import pytest
import subprocess
from pathlib import Path
from typing import NamedTuple
class TestCase(NamedTuple):
rule_path: Path
fixture_path: Path
should_match: bool
def load_test_cases() -> list[TestCase]:
"""Carica tutti i test case dalla struttura di directory."""
test_cases = []
rules_dir = Path("rules")
fixtures_dir = Path("tests/fixtures")
for rule_file in rules_dir.rglob("*.yml"):
rel_path = rule_file.relative_to(rules_dir)
fixture_dir = fixtures_dir / rel_path.parent / rule_file.stem
if fixture_dir.exists():
for pos_file in (fixture_dir / "positive").glob("*.json"):
test_cases.append(TestCase(rule_file, pos_file, True))
for neg_file in (fixture_dir / "negative").glob("*.json"):
test_cases.append(TestCase(rule_file, neg_file, False))
return test_cases
class TestDetectionRules:
"""Test suite per le detection rules."""
@pytest.mark.parametrize("test_case", load_test_cases())
def test_rule_against_fixture(self, test_case: TestCase):
rule_path, fixture_path, should_match = test_case
with open(fixture_path) as f:
log_event = json.load(f)
# Valuta la regola contro l'evento (implementazione omessa per brevita)
matched = evaluate_sigma_rule(rule_path, log_event)
if should_match:
assert matched, (
f"Rule {rule_path.name} SHOULD match {fixture_path.name} "
f"but did NOT match.\nEvent: {json.dumps(log_event, indent=2)}"
)
else:
assert not matched, (
f"Rule {rule_path.name} should NOT match {fixture_path.name} "
f"but DID match (false positive).\n"
f"Event: {json.dumps(log_event, indent=2)}"
)
def test_no_duplicate_ids(self):
"""Verifica che non ci siano UUID duplicati nel repository."""
seen_ids = {}
duplicates = []
for rule_file in Path("rules").rglob("*.yml"):
with open(rule_file) as f:
rule = yaml.safe_load(f)
rule_id = rule.get('id', '')
if rule_id in seen_ids:
duplicates.append(
f"{rule_file} duplicates ID of {seen_ids[rule_id]}"
)
else:
seen_ids[rule_id] = rule_file
assert not duplicates, "Duplicate IDs:\n" + "\n".join(duplicates)
def test_all_rules_have_tests(self):
"""Verifica che ogni regola abbia almeno un test positivo."""
rules_dir = Path("rules")
fixtures_dir = Path("tests/fixtures")
missing = []
for rule_file in rules_dir.rglob("*.yml"):
rel_path = rule_file.relative_to(rules_dir)
pos_dir = fixtures_dir / rel_path.parent / rule_file.stem / "positive"
if not pos_dir.exists() or not list(pos_dir.glob("*.json")):
missing.append(str(rule_file))
if missing:
pytest.fail(
"Rules missing positive test fixtures:\n" +
"\n".join(f" - {r}" for r in missing)
)
Testvorrichtungen: Beispiele für synthetische Protokolle
# tests/fixtures/windows/credential_access/
# cred_mimikatz_lsass/positive/mimikatz_direct.json
{
"EventID": 10,
"Channel": "Microsoft-Windows-Sysmon/Operational",
"SourceImage": "C:\\Users\\attacker\\Downloads\\mimikatz.exe",
"TargetImage": "C:\\Windows\\System32\\lsass.exe",
"GrantedAccess": "0x1010",
"UtcTime": "2026-03-09 14:30:00.123"
}
# cred_mimikatz_lsass/negative/windows_defender_scan.json
{
"EventID": 10,
"Channel": "Microsoft-Windows-Sysmon/Operational",
"SourceImage": "C:\\Program Files\\Windows Defender\\MsMpEng.exe",
"TargetImage": "C:\\Windows\\System32\\lsass.exe",
"GrantedAccess": "0x1000",
"UtcTime": "2026-03-09 14:31:00.456"
}
DaC-Qualitätsmetriken
| Metrica | Definition | Target |
|---|---|---|
| Detection Coverage | % ATT&CK-Techniken, die von einer stabilen Regel abgedeckt werden | > 60% |
| Test Coverage | %-Regeln mit positiven und negativen Tests | 100% |
| False Positive Rate | FP-Alarme / Gesamtalarme pro Woche | < 10% |
| Deployment Frequency | Zusammenführen mit neuen Regeln pro Woche | > 3/settimana |
| Time to Detection (TTD) | Zeit von der veröffentlichten Technik bis zur bereitgestellten Regel | < 48h (alta severita) |
Zu vermeidende Anti-Patterns
- Ohne Tests bereitstellen: auch im Notfall mindestens ein manueller Rauchtest
- Secrets in chiaro: usa sempre GitHub Secrets, mai hardcoded
- Pipeline ohne Benachrichtigungen: ein stiller und gefährlicher gescheiterter Einsatz
- Regeln ohne Besitzer: Verwenden Sie CODEOWNERS für jeden technischen Bereich
- Nur positive Tests: Ohne negative Fixtures weiß man nicht, ob man FP generiert
Fazit
Detection-as-Code ist nicht nur ein Werkzeugwechsel, sondern ein Mentalitätswechsel: Erkennungen sind Code und müssen als solcher getestet, versioniert und überarbeitet werden und systematisch einsetzen. Eine ausgereifte DaC-Pipeline verkürzt die Zeit bis zur Erkennung. Erhöht die Qualität der Regeln und ermöglicht die Skalierung des Erkennungsprogramms ohne das Personal proportional aufzustocken.
Nächster Artikel der Serie
Im nächsten Artikel werden wir sehen, wie man integriert MITRE ATT&CK im DaC-Workflow, um Abdeckungslücken automatisch abzubilden und zu priorisieren Die neuen Erkennungen basieren auf dem tatsächlichen Risiko.







