Engineering

SOAR-Playbook-Design: Reaktive Datenflüsse, Conditional Branching und das Argument für Dry-Run

Die meisten SOAR-Playbooks sind brüchige Skripte in hübscher Verpackung. Die, die den Kontakt mit Produktion überleben, teilen vier Merkmale: einen visuellen Designer mit reaktivem Datenfluss, einen typisierten Node-Vertrag, echtes Conditional Branching und einen Dry-Run-Modus, der die Logik ausführt, ohne die Seiteneffekte auszulösen. So funktioniert jedes davon — und was sich ändert, wenn man sie hat.

Von Krasper Engineering 10 Jun 2026 9 Min. Lesezeit
SOAR Playbook Design — hero

TL;DR — Ein SOAR-Playbook soll die Codifizierung dessen sein, was die:der beste Analyst:in um 3 Uhr morgens tut. In der Praxis driften die meisten Playbooks in einen von zwei Failure-Modes: zu starr, um die Variation echter Incidents zu handlen — oder so generisch, dass sie nichts Substanzielles automatisieren. Vier Design-Entscheidungen — reaktiver Datenfluss, typisierter Node-Vertrag, First-Class Conditional Branching und ein nicht-destruktiver Dry-Run-Modus — trennen Playbooks, die genutzt werden, von Playbooks, die jedes Quartal neu geschrieben werden.

Das Versprechen von Security-Orchestrierung ist einfach: Codifiziere das Runbook, feure es auf den Alert, lass die Plattform die mechanische Arbeit übernehmen, gib der:dem Analyst:in Raum zum Denken. Die Realität ist messier. Echte Incidents passen nicht zum Runbook, weil echte Incidents Variation haben — dieselbe Phishing-Kampagne erreicht drei User mit leicht unterschiedlichen Mail-Headern, die Hälfte der URLs ist bei Triage-Beginn schon neutralisiert, das EDR hat zwei Endpoints geflagged aber nicht den dritten, der:die User:in im Finance-Team hat Approver-Rechte, die das Containment-Kalkül ändern.

Ein Playbook, das keine Variation handlen kann, ist ein Skript. Ein Skript, das nicht sicher geändert werden kann, ist eine Belastung. Eine Belastung, die Produktivsysteme anfasst, ist der Grund, warum SOAR-Projekten nach dem ersten Jahr stillschweigend nicht mehr vertraut wird.

Dieser Beitrag geht durch die vier Design-Entscheidungen, die in unserer Erfahrung beim Bau des Krasper-Suite-Playbook-Designers Playbooks, die unter echtem Traffic halten, von denen trennen, die umgangen werden. Die Rahmung ist technisch; die Beispiele sind absichtlich konkret.

Inhalt

  1. Warum die meisten Playbooks verfallen
  2. Reaktiver Datenfluss — warum der Editor zählt
  3. Der Node-Vertrag — typisierte Ports, explizite Failure-Modes
  4. Conditional Branching als First-Class-Primitive
  5. Durchgespieltes Beispiel — Phishing-Response, end-to-end
  6. Dry-Run-Mechanik — Testen ohne Sprengradius
  7. Was sich ändert, wenn man alle vier hat/products/suite/

1. Warum die meisten Playbooks verfallen

Das Verfalls-Muster ist über Organisationen hinweg konsistent. Ein Playbook wird gegen ein sauberes mentales Modell des Incidents geschrieben. Es läuft gut in der Demo, gut in der ersten Woche, gut bei den Incidents, die wie der aussehen, den die:der Autor:in im Kopf hatte.

Dann kommen die Variationen.

Der Mail-Header-Parser wurde für das Cloud-Mail-Gateway geschrieben und bricht bei der On-Prem-Appliance. Die IP-zu-Asset-Enrichment liefert unknown für Cloud-Workloads. Der Containment-Schritt nimmt an, dass Endpoint-Isolation funktioniert — was sie tut, bis das Gerät offline ist. Der Notification-Schritt nimmt an, dass die:der Channel-Responder:in on call ist — was um 3 Uhr morgens am Samstag nicht der Fall ist.

Jede Variation produziert eine Exception. Exceptions sammeln sich als aufgesetzte Conditional-Logik, tief verschachtelt, zunehmend nicht mehr reviewbar. Nach sechs Monaten ist das Playbook eine 2.000-Zeilen-YAML-Datei, die ein:e Engineer versteht und nur ungern modifiziert. Das Team beginnt, drumherum zu arbeiten.

Die Wurzel ist selten die Logik selbst. Es ist die Repräsentation. Ein Playbook als lineares Skript mit wachsenden Conditionals zu repräsentieren ist eine Repräsentation, die ihre eigene Evolution bekämpft. Ein Playbook als typisierter gerichteter Graph mit reaktivem Datenfluss tut das nicht.

2. Reaktiver Datenfluss — warum der Editor zählt

Der visuelle Editor wird oft als UX-Spielerei abgetan. Ist er nicht. Er ist eine Forcing-Function für ein spezifisches Datenfluss-Modell, das lineare Skripte nicht erzwingen.

In einem reaktiven Node-Graph-Editor deklariert jede Node ihre Inputs und Outputs als benannte, typisierte Ports. Verbindungen zwischen Nodes sind explizit. Die Runtime läuft den Graphen ab, evaluiert eine Node, sobald alle ihre Inputs erfüllt sind, propagiert die Outputs zu Downstream-Nodes. Es gibt keinen versteckten State; der Graph ist das Programm.

┌──────────────┐       ┌──────────────────┐       ┌──────────────┐
│  Alert       │       │  Enrich asset    │       │  Severity    │
│  ingress     ├──────▶│                  ├──────▶│  classifier  │
│              │       │  in: ip          │       │              │
│  out: alert  │       │  out: asset      │       │  out: level  │
└──────────────┘       └──────────────────┘       └──────┬───────┘
                                                            │
                          ┌─────────────────────────────────┴───┐
                          │                                     │
                          ▼                                     ▼
                   ┌────────────┐                       ┌────────────┐
                   │  Contain   │                       │  Notify    │
                   │  (high)    │                       │  (medium)  │
                   └────────────┘                       └────────────┘
Reaktiver Node-Graph — typisierte Ports, explizite Edges

Was diese Repräsentation gegenüber einem Skript bringt:

  • Der Graph ist der Vertrag. Ein:e Reviewer:in kann ihn lesen. Ein:e Junior kann eine Node ändern, ohne das Ganze zu verstehen.
  • Seiteneffekte sind lokalisiert. Eine Node, die Produktion anfasst, ist eine einzelne, identifizierbare Form auf dem Canvas. Umgebende Nodes sind reine Transformationen.
  • Diffability ist strukturell. Eine Änderung am Graphen ist eine Änderung an einer Menge Nodes und Edges — nicht eine Änderung in einem Tausend-Zeilen-Skript mit verschiebender Indentation. Pull-Requests gegen Playbooks werden reviewbar.

Der reaktive Teil zählt, weil der Editor den aktuellen Daten-State, der durch jeden Port fließt, anzeigen kann, während die:der Autor:in das Playbook gegen einen Sample-Alert baut. Die:der Autor:in sieht in Echtzeit, welche Form asset hat, wenn es den Classifier erreicht — nicht die Form, die die Doku behauptet. Die meisten Playbook-Bugs entstehen durch Drift zwischen dokumentierten und tatsächlichen Daten-Shapes; reaktives Feedback kollabiert diese Lücke.

3. Der Node-Vertrag — typisierte Ports, explizite Failure-Modes

Die Graph-Metapher bringt nur etwas, wenn die Nodes darin einem engen Vertrag folgen. Unser Node-Vertrag hat vier Pflichteigenschaften.

Typisierte Inputs und Outputs. Jeder Port hat ein deklariertes Schema. Die Runtime weigert sich, einen Port, der IpAddress outputtet, mit einem zu verbinden, der EmailMessage erwartet. Klingt offensichtlich. Die Hälfte aller Playbook-Failures, die wir in Audits beobachtet haben, lässt sich auf Runtime-Type-Mismatches zurückführen, die der Editor zur Design-Time hätte verhindern können.

Explizite Failure-Outputs. Jede Node, die ein externes System anfasst, hat einen success-Port und einen error-Port. Der Error-Port ist nicht optional. Verdrahtet ihn die:der Autor:in nicht, flaggt der Editor die Node als unvollständig. Das ist die einzelne Design-Entscheidung, die die meisten Produktiv-Incidents verhindert hat — sie zwingt die:den Autor:in, den Unhappy-Path zur Design-Time zu bedenken, nicht erst nach der ersten 2-Uhr-morgens-Page.

Idempotenz by Default. Side-Effect-Nodes tragen einen automatisch generierten Idempotency-Key, abgeleitet aus Playbook-Execution-ID und Node-ID. Replays derselben Execution gegen dieselbe Node deduplizieren auf Runtime-Ebene. Autor:innen müssen nicht darüber nachdenken; die Plattform garantiert es.

Deterministische Timeouts. Jeder externe Call hat ein deklariertes Timeout. Nodes, die ihr Timeout überschreiten, routen zum Error-Port, nicht zu einer hängenden Execution. Es gibt keinen "ich warte für immer auf die EDR-API"-Failure-Mode, weil die Runtime die Node vorher killt.

Eine Node, die alle vier Eigenschaften erfüllt, ist komponierbar. Eine Node, die keine erfüllt, ist der Grund, warum Playbooks verfallen.

4. Conditional Branching als First-Class-Primitive

Die meisten frühen SOAR-Produkte behandelten Conditionals als Nachgedanken — eine Node mit einem "if"-Ausdruck, Output zu einer einzelnen Downstream-Node geroutet. Das funktioniert für ein oder zwei Branches. Bei Scale kollabiert es.

Ein First-Class-Branching-Primitive sieht so aus:

  • Eine switch-Node mit N deklarierten Outputs, jeder durch ein typisiertes Prädikat über den Inputs bewacht.
  • Die Runtime evaluiert Prädikate in deklarierter Reihenfolge, routet zum ersten Match und kurzschließt den Rest.
  • Ein erforderlicher default-Output fängt alles, was auf kein Prädikat matcht. Der Editor weigert sich, das Playbook ohne ihn zu speichern.
  • Die Output-Ports des Switches tragen verengte Typen — downstream vom "Alert kommt vom Cloud-Mail-Gateway"-Branch verengt sich der alert-Port-Typ auf CloudMailAlert, und die Downstream-Nodes sehen nur noch die Felder, auf die sie sich verlassen können.
┌────────────────────────────┐
│  switch on alert.source    │
│                            │
│  case cloud_mail  ────────┼──▶ cloud-mail subgraph
│  case onprem_mail ────────┼──▶ onprem subgraph
│  case api_gateway ────────┼──▶ api subgraph
│  default          ────────┼──▶ unknown-source handler
└────────────────────────────┘
Switch-Node mit Pflicht-Default und type-narrowed Outputs

Type Narrowing ist der Teil, der ein echtes Branching-Primitive von einem kosmetischen unterscheidet. Damit lässt sich jeder Subgraph gegen einen engeren Vertrag schreiben, und der Editor fängt Integrationsfehler zur Design-Time. Ohne ihn muss jeder Subgraph defensiv jedes Feld neu prüfen — genau der Failure-Mode, der Playbooks zu unlesbaren Conditionals aufbläht.

Der default-Branch ist nicht ohne Grund Pflicht. Unbehandelte Fälle in Produktion um 3 Uhr morgens produzieren keinen Wert. Entweder entscheidet das Playbook, was zu tun ist, oder es eskaliert explizit an einen Menschen. Beides ist akzeptabel; stilles Durchfallen ist es nicht.

5. Durchgespieltes Beispiel — Phishing-Response, end-to-end

Phishing ist das Standard-Beispiel, weil jedes SOC es betreibt und die Variations-Fläche groß ist. Das Skelett eines echten Playbooks sieht so aus:

[Alert ingress: mail-security signal]
        │
        ▼
[Parse headers + extract URLs/attachments]
        │
        ▼
[Enrich: sender reputation, domain age, URL reputation]
        │
        ▼
[switch: phishing_confidence]
   ├── high   → [Quarantine mail across all mailboxes]
   │              │
   │              ▼
   │           [Identify all recipients] ── for each ──▶
   │              ├── [User in privileged group?] ── yes ──▶ [Force password reset]
   │              │                                 └── no  ──▶ [Notify user via approved channel]
   │              ▼
   │           [Endpoint check: any URL clicked?] ── yes ──▶ [Isolate endpoint + open IR ticket]
   │
   ├── medium → [Quarantine for sender, flag for analyst review]
   │
   ├── low    → [Tag for trend analysis, no user impact]
   │
   └── default → [Hold for analyst, no automatic action]
Phishing-Response-Playbook — Skelett

Ein paar Dinge zu dieser Form.

Der Enrichment-Schritt ist eine Node, nicht drei — die Plattform handlet Parallel-Fan-Out intern. Die:der Autor:in schreibt keinen Concurrency-Code; sie:er deklariert, dass der Classifier sender_reputation, domain_age und url_reputation braucht, und die Runtime löst die Dependencies parallel auf, wo sie kann.

Die "for each recipient"-Schleife ist eine einzelne Iterator-Node mit angehängtem Subgraph. Der Subgraph ist selbst reviewbar, diffbar, isoliert testbar. Die:der Autor:in schreibt keine Schleifen; sie:er deklariert "dieser Subgraph läuft einmal pro Empfänger:in."

Der Privileged-User-Check ist ein Switch mit zwei Outcomes, beide explizit behandelt. Es gibt kein implizites Durchfallen, bei dem ein:e privilegierte:r User:in versehentlich den Standard-Notification-Pfad bekommt.

Der Endpoint-Isolation-Schritt hat einen Error-Port (oben nicht gezeigt), der zu einem Analyst-Alert routet, falls Isolation fehlschlägt — etwa weil der Endpoint offline ist. Das Playbook nimmt nie stillschweigend an, dass seine Containment-Aktion geglückt ist.

6. Dry-Run-Mechanik — Testen ohne Sprengradius

Das einzelne Feature, das entscheidet, ob Engineers tatsächlich an Playbooks iterieren, ist Dry-Run.

Ein Dry-Run führt den gesamten Graphen gegen einen echten oder gesampleten Alert aus, läuft jede Node ab, evaluiert jedes Prädikat, propagiert jeden Wert — aber ruft die Side-Effect-APIs nicht auf. Nodes, die Mail quarantänen, Endpoints isolieren, Passwort-Resets erzwingen oder Tickets öffnen würden, emittieren stattdessen einen Record, was sie getan hätten, und reichen den synthetischen Success-Output weiter.

Das Implementierungsmuster ist einfach: jeder Side-Effect-Adapter checkt am Eintritt ein execution_mode-Flag. Im live-Modus ruft er die Upstream-API. Im dry_run-Modus liefert er eine deterministische synthetische Response zurück, die dem Schema eines echten Success entspricht, und schreibt den intendierten Call in den Execution-Trace.

text
┌────────────────────────────────────┐
│ Side-effect adapter                │
│                                    │
│ if mode == "dry_run":              │
│   record_intended_call(args)       │
│   return synthetic_success(args)   │
│                                    │
│ else:                              │
│   call_real_api(args)              │
└────────────────────────────────────┘
Side-Effect-Adapter — mode-aware Einstiegspunkt

Die Vorteile kumulieren:

  • Autor:innen können ein Playbook gegen den tatsächlichen Phishing-Alert der vergangenen Woche laufen lassen und den vollen Execution-Trace sehen, bevor sie die Änderung pushen.
  • CI kann bei jeder Änderung einen Korpus historischer Alerts gegen das neue Playbook abspielen und den Build fehlschlagen lassen, wenn eine Execution unerwartet von der Vorversion abweicht.
  • Incident-Retros können den exakten Alert gegen das aktuelle Playbook abspielen, um zu bestätigen, dass der Fix greift.

Eine Design-Disziplin ist nötig: Dry-Run-Integrität ist eine Plattform-Garantie, keine Per-Node-Garantie. Jeder Side-Effect-Adapter, der ausgeliefert wird, muss das Mode-Flag respektieren, und die Runtime erzwingt es an der Adapter-Grenze, nicht im Ermessen der:des Autor:in. Ein einzelner Adapter, der Dry-Run ignoriert und stillschweigend Produktion ruft, macht den gesamten Modus zur Belastung. Adapter-Test-Suites verifizieren beide Modi bei jedem Release.

7. Was sich ändert, wenn man alle vier hat

Die vier Teile — reaktiver Datenfluss, typisierter Node-Vertrag, First-Class-Branching, Dry-Run — sind einzeln nützlich und gemeinsam transformativ. Die Verschiebung zeigt sich an zwei messbaren Stellen.

Mean Time to Respond. Wenn das Playbook Variation nativ handlet, verschwinden die manuellen Handoff-Schritte, die zuvor die Ausführung gegated haben. Für ein Phishing-Playbook der obigen Form ist die typische Beobachtung über Betriebsumgebungen hinweg, dass der automatisierbare Teil der Triage von der Größenordnung mehrerer Minuten (menschengetrieben) auf die Größenordnung von Sekunden (end-to-end automatisiert) fällt, weil die:der Analyst:in die frühe Klassifizierungsentscheidung nicht mehr von Hand treffen muss. Die verbleibende Menschen-Zeit konzentriert sich auf die wirklich mehrdeutigen Fälle, die das Playbook absichtlich eskaliert — genau dorthin gehört Analyst:innen-Aufmerksamkeit.

Mean Time to Change. Das ist die Metrik, die die meisten Teams zu messen vergessen. Ein Playbook, dessen sichere Modifikation eine Woche dauert, ist ein Playbook, das das Team vermeidet zu modifizieren. Mit Dry-Run plus strukturellen Diffs schrumpft die Modifikations-Loop typischerweise von Tagen auf unter eine Stunde: Graph editieren, gegen einen Korpus dry-runnen, strukturellen Diff reviewen, shippen. Der Downstream-Effekt: das Playbook hält tatsächlich Schritt mit der Bedrohungslandschaft, statt immer weiter dahinter zurückzufallen.

Die Zahlen hängen stark vom Baseline und der Integrationsqualität des umgebenden Stacks ab — wer ein einzelnes Before/After-Vielfaches zitiert, verkauft eine Folie, misst kein Outcome. Die ehrliche Rahmung: die Richtung ist über die von uns beobachteten Umgebungen konsistent; die Magnitude variiert.

Abschluss

Ein SOAR-Produkt sind nicht die Playbooks, mit denen es ausgeliefert wird. Es ist das Autoring-Substrat — der Editor, der Node-Vertrag, die Branching-Primitives, die Dry-Run-Garantie. Die Playbooks selbst sind, wie dieses Substrat genutzt wird.

Ist das Substrat solide, entwickeln sich die Playbooks. Ist es das nicht, ossifizieren die Playbooks, das Team arbeitet drumherum, und das Investment hört stillschweigend auf, Wert zurückzuliefern.

Der nächste Beitrag in dieser Serie geht eine Schicht tiefer — in die Runtime selbst, wie Executions persistiert werden, wie Partial Failures recovered werden und warum das Execution-Log das wichtigste Audit-Artefakt der Plattform ist.

Weiterlesen

  • NIST SP 800-61r3 — Computer Security Incident Handling Guide
  • MITRE D3FEND — Detection, Denial, Disruption Framework
  • Eric Evans — Domain-Driven Design, Kapitel zu Intention-Revealing Interfaces
Zurück zum Blog

Bereit, Ihre
Unternehmensinfrastruktur abzusichern?

Vereinbaren Sie ein technisches Briefing. Kein Sales-Pitch — nur Architekten und Ihr Team.