feat(soar): seed fusion-era default playbooks

Day 1 DB now ships with two playbooks that exercise the Fusion v1
condition vocabulary so a fresh install demonstrates multi-source
agreement out of the box instead of requiring the operator to author a
MultiSourceMin / SingleSourceHigh playbook before anything blocks.

Both target `c2_beacon` — the canonical attack type where fusion carries
the highest marginal value (Suricata trojan-activity + Beaconing CV +
ML c2 class can all independently fire on the same C2 channel):

- `fusion_c2_multi_source_block` — Condition: MultiSourceMin >= 2.
  Actions: block_ip 3600s + telegram + log. Requires agreement from
  two independent detection sources before blocking.
- `fusion_c2_suricata_solo_high_block` — Condition: SingleSourceHigh
  for Suricata with min_confidence 0.95. Actions: block_ip 3600s +
  telegram + log. Escape hatch for high-certainty signature hits
  that shouldn't wait for a second source.

Idempotent via the existing "seed only when playbooks table is empty"
guard in `seed_default_playbooks`.

Note: the pre-existing `default_block` (trigger "threat_detected")
never matches any fused event — canonicalization in the orchestrator
rewrites attack_type to one of the 13 canonical values before SOAR
sees it. That cleanup is separate; this commit only adds the fusion
defaults.

clippy --package net-guardia -- -D warnings clean. 219 pass.

Closes A-region F-5 seed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
DaLaw2 2026-04-18 17:21:06 +08:00
parent 88d93f363a
commit 7e7d47f94c

View File

@ -1565,6 +1565,28 @@ impl Database {
self.insert_playbook_action(pb3, 2, "log", r#"{"level": "warn"}"#)?;
self.insert_playbook_condition(pb3, "threshold", ">=", "0.7", None)?;
// 4. fusion_c2_multi_source_block — C2 beacon observed by ≥2 sources
// (e.g. Suricata trojan-activity + Beaconing CV + ML c2 class) is
// the highest-precision fusion signal we ship. Block for 1h and
// notify, no solo-source threshold so single-source C2 hits still
// require the solo playbook below to act.
let pb4 = self.insert_playbook("fusion_c2_multi_source_block", "c2_beacon", None, None, None, 600)?;
self.insert_playbook_action(pb4, 1, "block_ip", r#"{"ttl_secs": 3600}"#)?;
self.insert_playbook_action(pb4, 2, "send_telegram", "{}")?;
self.insert_playbook_action(pb4, 3, "log", r#"{"level": "warn"}"#)?;
self.insert_playbook_condition(pb4, "multi_source_min", ">=", "2", None)?;
// 5. fusion_c2_suricata_solo_high_block — the escape hatch for
// Suricata signature hits with very high confidence (>=0.95).
// Lets known-good rules fire without waiting for agreement from a
// second source, matching how analysts intuitively treat a
// signature "dead-on" match.
let pb5 = self.insert_playbook("fusion_c2_suricata_solo_high_block", "c2_beacon", None, None, None, 600)?;
self.insert_playbook_action(pb5, 1, "block_ip", r#"{"ttl_secs": 3600}"#)?;
self.insert_playbook_action(pb5, 2, "send_telegram", "{}")?;
self.insert_playbook_action(pb5, 3, "log", r#"{"level": "warn"}"#)?;
self.insert_playbook_condition(pb5, "single_source_high", "==", "Suricata", Some("0.95"))?;
Ok(())
}