refactor(soar): drop dead default_block seed playbook

default_block had trigger_event="threat_detected", but post-fusion events
carry only canonical dictionary strings (brute_force, c2_beacon, port_scan,
etc.). The string "threat_detected" isn't in the dictionary, so the
playbook never matched anything and logged NonCanonicalTriggerEvent on
every SOAR reload. The four remaining seed playbooks already cover the
blocking semantics via canonical triggers (brute_force_block +
fusion_c2_multi_source_block + fusion_c2_suricata_solo_high_block).

Updated two SOAR tests that depended on default_block matching
"threat_detected" to instead target fusion_c2_multi_source_block with a
2-source c2_beacon event, which matches fusion-era semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
DaLaw2 2026-04-18 18:17:26 +08:00
parent 5f6a65c452
commit 47d73cb07a
2 changed files with 33 additions and 38 deletions

View File

@ -1552,46 +1552,40 @@ impl Database {
}
drop(conn);
// 1. default_block: threat_detected, threshold 0.85 → block_ip(1800s) + log
let pb1 = self.insert_playbook("default_block", "threat_detected", Some(0.85), None, None, 300)?;
self.insert_playbook_action(pb1, 1, "block_ip", r#"{"ttl_secs": 1800}"#)?;
self.insert_playbook_action(pb1, 2, "log", r#"{"level": "warn"}"#)?;
self.insert_playbook_condition(pb1, "threshold", ">=", "0.85", None)?;
// 1. brute_force_block: brute_force, count 5 in 60s → block_ip(3600s) + send_telegram + log
let pb1 = self.insert_playbook("brute_force_block", "brute_force", None, Some(5), Some(60), 600)?;
self.insert_playbook_action(pb1, 1, "block_ip", r#"{"ttl_secs": 3600}"#)?;
self.insert_playbook_action(pb1, 2, "send_telegram", "{}")?;
self.insert_playbook_action(pb1, 3, "log", r#"{"level": "warn"}"#)?;
self.insert_playbook_condition(pb1, "frequency", ">=", "5", Some("60"))?;
// 2. brute_force_block: brute_force, count 5 in 60s → block_ip(3600s) + send_telegram + log
let pb2 = self.insert_playbook("brute_force_block", "brute_force", None, Some(5), Some(60), 600)?;
self.insert_playbook_action(pb2, 1, "block_ip", r#"{"ttl_secs": 3600}"#)?;
self.insert_playbook_action(pb2, 2, "send_telegram", "{}")?;
self.insert_playbook_action(pb2, 3, "log", r#"{"level": "warn"}"#)?;
self.insert_playbook_condition(pb2, "frequency", ">=", "5", Some("60"))?;
// 2. port_scan_alert: port_scan, threshold 0.7 → send_telegram + log (no block)
let pb2 = self.insert_playbook("port_scan_alert", "port_scan", Some(0.7), None, None, 300)?;
self.insert_playbook_action(pb2, 1, "send_telegram", "{}")?;
self.insert_playbook_action(pb2, 2, "log", r#"{"level": "warn"}"#)?;
self.insert_playbook_condition(pb2, "threshold", ">=", "0.7", None)?;
// 3. port_scan_alert: port_scan, threshold 0.7 → send_telegram + log (no block)
let pb3 = self.insert_playbook("port_scan_alert", "port_scan", Some(0.7), None, None, 300)?;
self.insert_playbook_action(pb3, 1, "send_telegram", "{}")?;
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
// 3. 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)?;
let pb3 = self.insert_playbook("fusion_c2_multi_source_block", "c2_beacon", None, None, None, 600)?;
self.insert_playbook_action(pb3, 1, "block_ip", r#"{"ttl_secs": 3600}"#)?;
self.insert_playbook_action(pb3, 2, "send_telegram", "{}")?;
self.insert_playbook_action(pb3, 3, "log", r#"{"level": "warn"}"#)?;
self.insert_playbook_condition(pb3, "multi_source_min", ">=", "2", None)?;
// 5. fusion_c2_suricata_solo_high_block — the escape hatch for
// 4. 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"))?;
let pb4 = self.insert_playbook("fusion_c2_suricata_solo_high_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, "single_source_high", "==", "Suricata", Some("0.95"))?;
Ok(())
}

View File

@ -651,22 +651,23 @@ mod tests {
let event = ThreatDetectedEvent {
source_ip: "1.2.3.4".to_string(),
dest_ip: "10.0.0.1".to_string(),
attack_type: "threat_detected".to_string(),
attack_type: "c2_beacon".to_string(),
confidence: 0.95,
flow_count: 1,
packet_rate: 0.0,
protocol: 6,
geoip_country: None,
is_repeat_offender: false,
sources: vec![DetectionSource::ML],
active_source_count: 1,
fused_confidence: 0.95,
sources: vec![DetectionSource::Suricata, DetectionSource::ML],
active_source_count: 2,
fused_confidence: 0.97,
ae_score: 0.0,
anomaly_score: 0.0,
c2_score: 0.0,
};
// Find a matching playbook — default "threat_detected" playbook should exist
// The multi-source c2_beacon seed playbook matches a 2-source fusion
// event and its first action is block_ip.
let playbooks = engine.find_matching_playbooks(&event);
assert!(!playbooks.is_empty(), "Should have matching playbooks");
@ -698,16 +699,16 @@ mod tests {
let event = ThreatDetectedEvent {
source_ip: "1.2.3.4".to_string(),
dest_ip: "10.0.0.1".to_string(),
attack_type: "threat_detected".to_string(),
attack_type: "c2_beacon".to_string(),
confidence: 0.95,
flow_count: 1,
packet_rate: 0.0,
protocol: 6,
geoip_country: None,
is_repeat_offender: false,
sources: vec![DetectionSource::ML],
active_source_count: 1,
fused_confidence: 0.95,
sources: vec![DetectionSource::Suricata, DetectionSource::ML],
active_source_count: 2,
fused_confidence: 0.97,
ae_score: 0.0,
anomaly_score: 0.0,
c2_score: 0.0,