mirror of
https://github.com/DaLaw2/NetGuardia.git
synced 2026-08-24 14:10:28 +09:00
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:
parent
5f6a65c452
commit
47d73cb07a
@ -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(())
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user