_hope_core/
evolutionary_guard.rs

1//! # Hope Genome v2.1 - Evolutionary Guard ("Singularity")
2//!
3//! **RECURSIVE SELF-EVOLUTION** - The system that rewrites itself
4//!
5//! ## Philosophy
6//!
7//! Traditional security: Humans write rules, attackers find holes.
8//! Evolutionary Guard: The system LEARNS from attacks and evolves.
9//!
10//! ```text
11//! ┌────────────────────────────────────────────────────────────────┐
12//! │                    EVOLUTIONARY GUARD                          │
13//! │                   "Digital Immune System"                      │
14//! │                                                                │
15//! │   Attack ──▶ DenialProof ──▶ Pattern ──▶ Filter ──▶ Broadcast │
16//! │                                                                │
17//! │   "A támadó mire kiismerné a védelmet,                        │
18//! │    az már nem is létezik."                                    │
19//! │                                                                │
20//! │   Every second, the system gets SMARTER.                      │
21//! │   The code itself is POLYMORPHIC - constantly mutating.       │
22//! └────────────────────────────────────────────────────────────────┘
23//! ```
24//!
25//! ## Components
26//!
27//! - **AttackPattern**: Extracted signature from a failed attack
28//! - **ImmunityMemory**: Persistent storage of learned threats
29//! - **FilterGenerator**: Creates new defensive filters (WASM-ready)
30//! - **MutationEngine**: Polymorphic code transformation
31//! - **EvolutionaryGuard**: The immune system core
32//!
33//! ---
34//!
35//! **Date**: 2026-01-01
36//! **Version**: 2.1.0 (Singularity)
37//! **Authors**: Máté Róbert + Claude
38
39use crate::crypto::{KeyStore, SoftwareKeyStore};
40use crate::proof::ActionType;
41use crate::watchdog::DenialProof;
42use parking_lot::RwLock;
43use serde::{Deserialize, Serialize};
44use sha2::{Digest, Sha256};
45use std::collections::HashMap;
46use std::sync::atomic::{AtomicU64, Ordering};
47use std::sync::Arc;
48use std::time::{SystemTime, UNIX_EPOCH};
49
50// ============================================================================
51// ATTACK PATTERN
52// ============================================================================
53
54/// Severity level of detected attack
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
56pub enum ThreatLevel {
57    /// Low threat - probing, reconnaissance
58    Low,
59    /// Medium threat - active exploitation attempt
60    Medium,
61    /// High threat - sophisticated attack
62    High,
63    /// Critical - coordinated or novel attack
64    Critical,
65}
66
67/// Category of attack pattern
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
69pub enum AttackCategory {
70    /// Unauthorized file access
71    FileAccess,
72    /// Command injection attempt
73    CommandInjection,
74    /// Privilege escalation
75    PrivilegeEscalation,
76    /// Data exfiltration
77    DataExfiltration,
78    /// Denial of service
79    DenialOfService,
80    /// Replay attack
81    Replay,
82    /// Timing attack
83    TimingAttack,
84    /// Unknown/novel attack
85    Unknown,
86}
87
88impl AttackCategory {
89    /// Convert from category code (for mesh sync)
90    pub fn from_code(code: u8) -> Self {
91        match code {
92            0 => AttackCategory::FileAccess,
93            1 => AttackCategory::CommandInjection,
94            2 => AttackCategory::PrivilegeEscalation,
95            3 => AttackCategory::DataExfiltration,
96            4 => AttackCategory::DenialOfService,
97            5 => AttackCategory::Replay,
98            6 => AttackCategory::TimingAttack,
99            _ => AttackCategory::Unknown,
100        }
101    }
102}
103
104/// Extracted attack pattern from DenialProof
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct AttackPattern {
107    /// Unique pattern ID
108    pub id: String,
109
110    /// Pattern signature (hash of attack characteristics)
111    pub signature: [u8; 32],
112
113    /// Attack category
114    pub category: AttackCategory,
115
116    /// Threat level
117    pub threat_level: ThreatLevel,
118
119    /// Action type that was blocked
120    pub action_type: ActionType,
121
122    /// Target patterns (file paths, commands, etc.)
123    pub target_patterns: Vec<String>,
124
125    /// Keyword triggers found
126    pub keywords: Vec<String>,
127
128    /// Timing characteristics (for timing attack detection)
129    pub timing_signature: Option<TimingSignature>,
130
131    /// Rule that caught this attack
132    pub triggered_rule: String,
133
134    /// First seen timestamp
135    pub first_seen: u64,
136
137    /// Number of times seen
138    pub occurrence_count: u64,
139
140    /// Confidence score (0.0 - 1.0)
141    pub confidence: f64,
142}
143
144/// Timing signature for timing attack detection
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct TimingSignature {
147    /// Average interval between attempts (ms)
148    pub avg_interval_ms: u64,
149    /// Variance in timing
150    pub variance: f64,
151    /// Is it suspiciously regular?
152    pub is_automated: bool,
153}
154
155impl AttackPattern {
156    /// Create new attack pattern from denial proof
157    pub fn from_denial(denial: &DenialProof, _additional_context: Option<&str>) -> Self {
158        let now = SystemTime::now()
159            .duration_since(UNIX_EPOCH)
160            .unwrap()
161            .as_secs();
162
163        // Extract keywords from the denied action
164        let keywords = Self::extract_keywords(&denial.denial_reason);
165
166        // Determine category based on action and keywords
167        let category = Self::categorize(&denial.action_type, &keywords);
168
169        // Calculate threat level
170        let threat_level = Self::assess_threat(&category, &keywords);
171
172        // Extract target patterns
173        let target_patterns = Self::extract_targets(&denial.denial_reason);
174
175        // Compute signature
176        let signature = Self::compute_signature(denial, &category, &keywords);
177
178        // Generate unique ID
179        let id = format!("AP-{:08x}-{:04x}", now as u32, rand::random::<u16>());
180
181        AttackPattern {
182            id,
183            signature,
184            category,
185            threat_level,
186            action_type: denial.action_type.clone(),
187            target_patterns,
188            keywords,
189            timing_signature: None,
190            triggered_rule: denial.violated_rule.clone(),
191            first_seen: now,
192            occurrence_count: 1,
193            confidence: 0.8, // Initial confidence
194        }
195    }
196
197    /// Extract keywords from action description
198    fn extract_keywords(description: &str) -> Vec<String> {
199        let dangerous_keywords = [
200            "password",
201            "secret",
202            "key",
203            "token",
204            "credential",
205            "admin",
206            "root",
207            "sudo",
208            "exec",
209            "eval",
210            "system",
211            "delete",
212            "drop",
213            "truncate",
214            "rm",
215            "format",
216            "inject",
217            "bypass",
218            "escalate",
219            "overflow",
220            "/etc/passwd",
221            "/etc/shadow",
222            ".ssh",
223            ".env",
224            "curl",
225            "wget",
226            "nc",
227            "netcat",
228            "bash",
229            "sh",
230        ];
231
232        let lower = description.to_lowercase();
233        dangerous_keywords
234            .iter()
235            .filter(|kw| lower.contains(*kw))
236            .map(|s| s.to_string())
237            .collect()
238    }
239
240    /// Categorize attack based on action type and keywords
241    fn categorize(action_type: &ActionType, keywords: &[String]) -> AttackCategory {
242        // Check keywords first
243        for kw in keywords {
244            match kw.as_str() {
245                "inject" | "exec" | "eval" | "system" | "bash" | "sh" => {
246                    return AttackCategory::CommandInjection;
247                }
248                "admin" | "root" | "sudo" | "escalate" => {
249                    return AttackCategory::PrivilegeEscalation;
250                }
251                "password" | "secret" | "key" | "token" | ".env" | ".ssh" => {
252                    return AttackCategory::DataExfiltration;
253                }
254                _ => {}
255            }
256        }
257
258        // Fall back to action type
259        match action_type {
260            ActionType::Read => AttackCategory::FileAccess,
261            ActionType::Write => AttackCategory::FileAccess,
262            ActionType::Delete => AttackCategory::DenialOfService,
263            ActionType::Execute => AttackCategory::CommandInjection,
264            ActionType::Network => AttackCategory::DataExfiltration,
265            ActionType::Custom(_) => AttackCategory::Unknown,
266        }
267    }
268
269    /// Assess threat level
270    fn assess_threat(category: &AttackCategory, keywords: &[String]) -> ThreatLevel {
271        // Critical keywords
272        let critical_keywords = ["passwd", "shadow", "sudo", "root", "admin"];
273        let high_keywords = ["exec", "eval", "system", "inject", "bypass"];
274
275        for kw in keywords {
276            if critical_keywords.iter().any(|c| kw.contains(c)) {
277                return ThreatLevel::Critical;
278            }
279            if high_keywords.iter().any(|h| kw.contains(h)) {
280                return ThreatLevel::High;
281            }
282        }
283
284        match category {
285            AttackCategory::PrivilegeEscalation => ThreatLevel::Critical,
286            AttackCategory::CommandInjection => ThreatLevel::High,
287            AttackCategory::DataExfiltration => ThreatLevel::High,
288            AttackCategory::DenialOfService => ThreatLevel::Medium,
289            AttackCategory::FileAccess => ThreatLevel::Medium,
290            AttackCategory::Replay => ThreatLevel::Medium,
291            AttackCategory::TimingAttack => ThreatLevel::Low,
292            AttackCategory::Unknown => ThreatLevel::Medium,
293        }
294    }
295
296    /// Extract target patterns (paths, commands)
297    fn extract_targets(description: &str) -> Vec<String> {
298        let mut targets = Vec::new();
299
300        // Extract file paths
301        for part in description.split_whitespace() {
302            if part.starts_with('/') || part.starts_with("./") || part.starts_with("..") {
303                targets.push(part.to_string());
304            }
305            if part.contains(".env") || part.contains(".ssh") || part.contains("passwd") {
306                targets.push(part.to_string());
307            }
308        }
309
310        targets
311    }
312
313    /// Compute pattern signature
314    fn compute_signature(
315        denial: &DenialProof,
316        category: &AttackCategory,
317        keywords: &[String],
318    ) -> [u8; 32] {
319        let mut hasher = Sha256::new();
320
321        // Include category
322        hasher.update([*category as u8]);
323
324        // Include action type hash
325        hasher.update(denial.action_hash);
326
327        // Include sorted keywords
328        let mut sorted_keywords = keywords.to_vec();
329        sorted_keywords.sort();
330        for kw in sorted_keywords {
331            hasher.update(kw.as_bytes());
332        }
333
334        hasher.finalize().into()
335    }
336
337    /// Check if this pattern matches another (for deduplication)
338    pub fn matches(&self, other: &AttackPattern) -> bool {
339        // Same signature = same attack
340        if self.signature == other.signature {
341            return true;
342        }
343
344        // Same category + overlapping keywords = similar attack
345        if self.category == other.category {
346            let overlap: usize = self
347                .keywords
348                .iter()
349                .filter(|k| other.keywords.contains(k))
350                .count();
351
352            if overlap >= 2 {
353                return true;
354            }
355        }
356
357        false
358    }
359}
360
361// ============================================================================
362// IMMUNITY MEMORY
363// ============================================================================
364
365/// Immunity Memory - Persistent storage of learned attack patterns
366///
367/// Like biological immune memory, remembers past threats.
368#[allow(dead_code)]
369pub struct ImmunityMemory {
370    /// Known attack patterns (signature -> pattern)
371    patterns: RwLock<HashMap<[u8; 32], AttackPattern>>,
372
373    /// Pattern occurrence counts
374    occurrences: RwLock<HashMap<[u8; 32], u64>>,
375
376    /// Total attacks blocked
377    total_blocked: AtomicU64,
378
379    /// Memory creation time
380    created_at: u64,
381
382    /// Last update time
383    last_update: RwLock<u64>,
384}
385
386impl ImmunityMemory {
387    /// Create new immunity memory
388    pub fn new() -> Self {
389        let now = SystemTime::now()
390            .duration_since(UNIX_EPOCH)
391            .unwrap()
392            .as_secs();
393
394        ImmunityMemory {
395            patterns: RwLock::new(HashMap::new()),
396            occurrences: RwLock::new(HashMap::new()),
397            total_blocked: AtomicU64::new(0),
398            created_at: now,
399            last_update: RwLock::new(now),
400        }
401    }
402
403    /// Record a new attack pattern
404    pub fn record(&self, pattern: AttackPattern) {
405        let signature = pattern.signature;
406
407        // Update occurrence count
408        {
409            let mut occurrences = self.occurrences.write();
410            *occurrences.entry(signature).or_insert(0) += 1;
411        }
412
413        // Store or update pattern
414        {
415            let mut patterns = self.patterns.write();
416            if let Some(existing) = patterns.get_mut(&signature) {
417                existing.occurrence_count += 1;
418                // Increase confidence with repeated sightings
419                existing.confidence = (existing.confidence + 0.05).min(1.0);
420            } else {
421                patterns.insert(signature, pattern);
422            }
423        }
424
425        self.total_blocked.fetch_add(1, Ordering::SeqCst);
426
427        let now = SystemTime::now()
428            .duration_since(UNIX_EPOCH)
429            .unwrap()
430            .as_secs();
431        *self.last_update.write() = now;
432    }
433
434    /// Check if a pattern is known
435    pub fn is_known(&self, signature: &[u8; 32]) -> bool {
436        self.patterns.read().contains_key(signature)
437    }
438
439    /// Get pattern by signature
440    pub fn get_pattern(&self, signature: &[u8; 32]) -> Option<AttackPattern> {
441        self.patterns.read().get(signature).cloned()
442    }
443
444    /// Get all patterns of a category
445    pub fn patterns_by_category(&self, category: AttackCategory) -> Vec<AttackPattern> {
446        self.patterns
447            .read()
448            .values()
449            .filter(|p| p.category == category)
450            .cloned()
451            .collect()
452    }
453
454    /// Get high-confidence patterns
455    pub fn high_confidence_patterns(&self, min_confidence: f64) -> Vec<AttackPattern> {
456        self.patterns
457            .read()
458            .values()
459            .filter(|p| p.confidence >= min_confidence)
460            .cloned()
461            .collect()
462    }
463
464    /// Get total patterns stored
465    pub fn pattern_count(&self) -> usize {
466        self.patterns.read().len()
467    }
468
469    /// Get total blocked count
470    pub fn total_blocked(&self) -> u64 {
471        self.total_blocked.load(Ordering::SeqCst)
472    }
473
474    /// Get most common attack categories
475    pub fn threat_statistics(&self) -> HashMap<AttackCategory, u64> {
476        let mut stats = HashMap::new();
477
478        for pattern in self.patterns.read().values() {
479            *stats.entry(pattern.category).or_insert(0) += pattern.occurrence_count;
480        }
481
482        stats
483    }
484}
485
486impl Default for ImmunityMemory {
487    fn default() -> Self {
488        Self::new()
489    }
490}
491
492// ============================================================================
493// POLYMORPHIC FILTER
494// ============================================================================
495
496/// A defensive filter that can mutate its representation
497#[derive(Debug, Clone, Serialize, Deserialize)]
498pub struct PolymorphicFilter {
499    /// Filter ID
500    pub id: String,
501
502    /// Generation number (increases with each mutation)
503    pub generation: u64,
504
505    /// Filter rules (what to block)
506    pub rules: Vec<FilterRule>,
507
508    /// Mutation seed (for deterministic polymorphism)
509    pub mutation_seed: u64,
510
511    /// Created timestamp
512    pub created_at: u64,
513
514    /// Parent filter ID (if mutated from another)
515    pub parent_id: Option<String>,
516
517    /// WASM bytecode (optional, for true WASM execution)
518    pub wasm_bytecode: Option<Vec<u8>>,
519}
520
521/// Individual filter rule
522#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct FilterRule {
524    /// Rule ID
525    pub id: String,
526
527    /// Attack category this rule targets
528    pub category: AttackCategory,
529
530    /// Keyword patterns to match
531    pub keyword_patterns: Vec<String>,
532
533    /// Regex patterns (as strings, compiled at runtime)
534    pub regex_patterns: Vec<String>,
535
536    /// Target path patterns
537    pub path_patterns: Vec<String>,
538
539    /// Action types to block
540    pub blocked_actions: Vec<ActionType>,
541
542    /// Minimum confidence to trigger
543    pub confidence_threshold: f64,
544}
545
546impl PolymorphicFilter {
547    /// Create new filter from attack patterns
548    pub fn from_patterns(patterns: &[AttackPattern]) -> Self {
549        let now = SystemTime::now()
550            .duration_since(UNIX_EPOCH)
551            .unwrap()
552            .as_secs();
553
554        let id = format!("PF-{:08x}-{:04x}", now as u32, rand::random::<u16>());
555
556        let rules: Vec<FilterRule> = patterns
557            .iter()
558            .map(|p| FilterRule {
559                id: format!("R-{}", &p.id[3..]),
560                category: p.category,
561                keyword_patterns: p.keywords.clone(),
562                regex_patterns: Vec::new(),
563                path_patterns: p.target_patterns.clone(),
564                blocked_actions: vec![p.action_type.clone()],
565                confidence_threshold: p.confidence,
566            })
567            .collect();
568
569        PolymorphicFilter {
570            id,
571            generation: 0,
572            rules,
573            mutation_seed: rand::random(),
574            created_at: now,
575            parent_id: None,
576            wasm_bytecode: None,
577        }
578    }
579
580    /// Check if an action should be blocked
581    pub fn should_block(&self, action_type: &ActionType, description: &str) -> Option<&FilterRule> {
582        let lower_desc = description.to_lowercase();
583
584        for rule in &self.rules {
585            // Check action type
586            if !rule.blocked_actions.contains(action_type) {
587                continue;
588            }
589
590            // Check keyword patterns
591            for keyword in &rule.keyword_patterns {
592                if lower_desc.contains(&keyword.to_lowercase()) {
593                    return Some(rule);
594                }
595            }
596
597            // Check path patterns
598            for path in &rule.path_patterns {
599                if description.contains(path) {
600                    return Some(rule);
601                }
602            }
603        }
604
605        None
606    }
607
608    /// Get rule count
609    pub fn rule_count(&self) -> usize {
610        self.rules.len()
611    }
612}
613
614// ============================================================================
615// MUTATION ENGINE
616// ============================================================================
617
618/// Mutation Engine - Creates polymorphic variants of filters
619///
620/// The same logic, different binary representation.
621/// Makes reverse-engineering nearly impossible.
622#[allow(dead_code)]
623pub struct MutationEngine {
624    /// Mutation counter
625    mutation_count: AtomicU64,
626
627    /// Random seed for mutations
628    seed: u64,
629}
630
631impl MutationEngine {
632    /// Create new mutation engine
633    pub fn new() -> Self {
634        MutationEngine {
635            mutation_count: AtomicU64::new(0),
636            seed: rand::random(),
637        }
638    }
639
640    /// Mutate a filter to create a new variant
641    ///
642    /// Preserves semantics, changes representation.
643    pub fn mutate(&self, filter: &PolymorphicFilter) -> PolymorphicFilter {
644        let mutation_id = self.mutation_count.fetch_add(1, Ordering::SeqCst);
645
646        let now = SystemTime::now()
647            .duration_since(UNIX_EPOCH)
648            .unwrap()
649            .as_secs();
650
651        let new_id = format!(
652            "PF-{:08x}-G{}-{:04x}",
653            now as u32,
654            filter.generation + 1,
655            rand::random::<u16>()
656        );
657
658        // Mutate rules while preserving logic
659        let mutated_rules: Vec<FilterRule> = filter
660            .rules
661            .iter()
662            .map(|r| self.mutate_rule(r, mutation_id))
663            .collect();
664
665        PolymorphicFilter {
666            id: new_id,
667            generation: filter.generation + 1,
668            rules: mutated_rules,
669            mutation_seed: rand::random(),
670            created_at: now,
671            parent_id: Some(filter.id.clone()),
672            wasm_bytecode: None, // Would regenerate WASM here
673        }
674    }
675
676    /// Mutate a single rule
677    fn mutate_rule(&self, rule: &FilterRule, mutation_id: u64) -> FilterRule {
678        // Create equivalent but different patterns
679        let mutated_keywords: Vec<String> = rule
680            .keyword_patterns
681            .iter()
682            .map(|kw| self.mutate_keyword(kw, mutation_id))
683            .collect();
684
685        FilterRule {
686            id: format!("{}-M{}", rule.id, mutation_id),
687            category: rule.category,
688            keyword_patterns: mutated_keywords,
689            regex_patterns: rule.regex_patterns.clone(),
690            path_patterns: rule.path_patterns.clone(),
691            blocked_actions: rule.blocked_actions.clone(),
692            confidence_threshold: rule.confidence_threshold,
693        }
694    }
695
696    /// Mutate a keyword pattern (add synonyms, variations)
697    fn mutate_keyword(&self, keyword: &str, _mutation_id: u64) -> String {
698        // In a real implementation, this would add synonyms,
699        // alternative spellings, etc.
700        // For now, we just return the original
701        keyword.to_string()
702    }
703
704    /// Get mutation count
705    pub fn mutation_count(&self) -> u64 {
706        self.mutation_count.load(Ordering::SeqCst)
707    }
708}
709
710impl Default for MutationEngine {
711    fn default() -> Self {
712        Self::new()
713    }
714}
715
716// ============================================================================
717// FILTER GENERATOR
718// ============================================================================
719
720/// Filter Generator - Creates new defensive filters from attack patterns
721pub struct FilterGenerator {
722    /// Signing key for filter authentication
723    keystore: SoftwareKeyStore,
724
725    /// Generated filter count
726    filter_count: AtomicU64,
727}
728
729impl FilterGenerator {
730    /// Create new filter generator
731    pub fn new() -> Result<Self, crate::crypto::CryptoError> {
732        Ok(FilterGenerator {
733            keystore: SoftwareKeyStore::generate()?,
734            filter_count: AtomicU64::new(0),
735        })
736    }
737
738    /// Generate filter from attack patterns
739    pub fn generate(&self, patterns: &[AttackPattern]) -> SignedFilter {
740        let filter = PolymorphicFilter::from_patterns(patterns);
741        self.filter_count.fetch_add(1, Ordering::SeqCst);
742
743        // Sign the filter
744        let filter_bytes = serde_json::to_vec(&filter).unwrap_or_default();
745        let signature = self.keystore.sign(&filter_bytes).unwrap_or_default();
746
747        SignedFilter {
748            filter,
749            signature,
750            generator_pubkey: self.keystore.public_key_bytes(),
751        }
752    }
753
754    /// Get generated filter count
755    pub fn filter_count(&self) -> u64 {
756        self.filter_count.load(Ordering::SeqCst)
757    }
758}
759
760/// A filter with cryptographic signature
761#[derive(Debug, Clone, Serialize, Deserialize)]
762pub struct SignedFilter {
763    /// The filter itself
764    pub filter: PolymorphicFilter,
765    /// Signature over filter
766    pub signature: Vec<u8>,
767    /// Generator's public key
768    pub generator_pubkey: Vec<u8>,
769}
770
771impl SignedFilter {
772    /// Verify filter signature
773    pub fn verify(&self, keystore: &dyn KeyStore) -> bool {
774        let filter_bytes = serde_json::to_vec(&self.filter).unwrap_or_default();
775        keystore.verify(&filter_bytes, &self.signature).is_ok()
776    }
777}
778
779// ============================================================================
780// EVOLUTIONARY GUARD (THE IMMUNE SYSTEM)
781// ============================================================================
782
783/// Evolutionary Guard - The self-evolving immune system
784///
785/// Learns from attacks, generates new defenses, and evolves constantly.
786pub struct EvolutionaryGuard {
787    /// Immunity memory
788    memory: Arc<ImmunityMemory>,
789
790    /// Active filters
791    active_filters: RwLock<Vec<SignedFilter>>,
792
793    /// Filter generator
794    generator: FilterGenerator,
795
796    /// Mutation engine
797    mutation_engine: MutationEngine,
798
799    /// Guard ID
800    id: String,
801
802    /// Is the guard active
803    active: std::sync::atomic::AtomicBool,
804
805    /// Evolution generation
806    generation: AtomicU64,
807
808    /// Broadcasts pending (for mesh distribution)
809    pending_broadcasts: RwLock<Vec<SignedFilter>>,
810}
811
812impl EvolutionaryGuard {
813    /// Create new evolutionary guard
814    pub fn new() -> Result<Self, crate::crypto::CryptoError> {
815        let id = format!("EG-{:08x}", rand::random::<u32>());
816
817        Ok(EvolutionaryGuard {
818            memory: Arc::new(ImmunityMemory::new()),
819            active_filters: RwLock::new(Vec::new()),
820            generator: FilterGenerator::new()?,
821            mutation_engine: MutationEngine::new(),
822            id,
823            active: std::sync::atomic::AtomicBool::new(true),
824            generation: AtomicU64::new(0),
825            pending_broadcasts: RwLock::new(Vec::new()),
826        })
827    }
828
829    /// Process a denial proof and learn from it
830    ///
831    /// This is the core learning function.
832    pub fn learn(&self, denial: &DenialProof) -> Option<SignedFilter> {
833        if !self.active.load(Ordering::SeqCst) {
834            return None;
835        }
836
837        // Extract attack pattern
838        let pattern = AttackPattern::from_denial(denial, None);
839
840        // Record in memory
841        self.memory.record(pattern.clone());
842
843        // Check if we should generate a new filter
844        let should_generate = match pattern.threat_level {
845            ThreatLevel::Critical => true,
846            ThreatLevel::High => pattern.occurrence_count >= 2,
847            ThreatLevel::Medium => pattern.occurrence_count >= 5,
848            ThreatLevel::Low => pattern.occurrence_count >= 10,
849        };
850
851        if should_generate {
852            // Generate new filter
853            let filter = self.generator.generate(&[pattern]);
854
855            // Add to active filters
856            self.active_filters.write().push(filter.clone());
857
858            // Queue for broadcast
859            self.pending_broadcasts.write().push(filter.clone());
860
861            // Increment generation
862            self.generation.fetch_add(1, Ordering::SeqCst);
863
864            return Some(filter);
865        }
866
867        None
868    }
869
870    /// Check if an action should be blocked by evolved filters
871    pub fn should_block(&self, action_type: &ActionType, description: &str) -> Option<String> {
872        for signed_filter in self.active_filters.read().iter() {
873            if let Some(rule) = signed_filter.filter.should_block(action_type, description) {
874                return Some(format!(
875                    "Blocked by evolved filter {} (rule: {}, category: {:?})",
876                    signed_filter.filter.id, rule.id, rule.category
877                ));
878            }
879        }
880        None
881    }
882
883    /// Evolve all filters (polymorphic mutation)
884    pub fn evolve(&self) {
885        let mut filters = self.active_filters.write();
886
887        let mutated: Vec<SignedFilter> = filters
888            .iter()
889            .map(|sf| {
890                let mutated_filter = self.mutation_engine.mutate(&sf.filter);
891                let filter_bytes = serde_json::to_vec(&mutated_filter).unwrap_or_default();
892                let signature = self
893                    .generator
894                    .keystore
895                    .sign(&filter_bytes)
896                    .unwrap_or_default();
897
898                SignedFilter {
899                    filter: mutated_filter,
900                    signature,
901                    generator_pubkey: self.generator.keystore.public_key_bytes(),
902                }
903            })
904            .collect();
905
906        *filters = mutated;
907        self.generation.fetch_add(1, Ordering::SeqCst);
908    }
909
910    /// Get pending broadcasts (for mesh distribution)
911    pub fn take_pending_broadcasts(&self) -> Vec<SignedFilter> {
912        std::mem::take(&mut *self.pending_broadcasts.write())
913    }
914
915    /// Receive filter from mesh network
916    pub fn receive_filter(&self, filter: SignedFilter) {
917        // In production, would verify signature against known generators
918        self.active_filters.write().push(filter);
919    }
920
921    /// Get immunity memory
922    pub fn memory(&self) -> Arc<ImmunityMemory> {
923        self.memory.clone()
924    }
925
926    /// Get current generation
927    pub fn generation(&self) -> u64 {
928        self.generation.load(Ordering::SeqCst)
929    }
930
931    /// Get active filter count
932    pub fn filter_count(&self) -> usize {
933        self.active_filters.read().len()
934    }
935
936    /// Get guard ID
937    pub fn id(&self) -> &str {
938        &self.id
939    }
940
941    /// Get threat statistics
942    pub fn threat_stats(&self) -> HashMap<AttackCategory, u64> {
943        self.memory.threat_statistics()
944    }
945
946    /// Broadcast immunity to the global mesh
947    ///
948    /// This method integrates with the Genesis Protocol's SyncProtocol
949    /// to propagate learned threats across the entire mesh network.
950    ///
951    /// # Arguments
952    ///
953    /// * `sync` - The SyncProtocol instance for mesh communication
954    ///
955    /// # Returns
956    ///
957    /// Number of filters broadcasted
958    pub fn broadcast_immunity(&self, sync: &crate::apex_protocol::SyncProtocol) -> usize {
959        if !self.active.load(Ordering::SeqCst) {
960            return 0;
961        }
962
963        // Take pending broadcasts
964        let filters = self.take_pending_broadcasts();
965        let count = filters.len();
966
967        // Broadcast each filter through the mesh
968        for filter in filters {
969            sync.broadcast_filter(filter);
970        }
971
972        // Also broadcast high-confidence patterns as threat fingerprints
973        let high_confidence = self.memory.high_confidence_patterns(0.9);
974        for pattern in high_confidence {
975            let fingerprint =
976                crate::apex_protocol::CompactedThreatFingerprint::from_pattern(&pattern, self.id());
977            sync.broadcast_threat(fingerprint);
978        }
979
980        count
981    }
982
983    /// Sync immunity from the mesh
984    ///
985    /// Receives and processes threat fingerprints from other nodes.
986    pub fn sync_from_mesh(
987        &self,
988        fingerprints: &[crate::apex_protocol::CompactedThreatFingerprint],
989    ) {
990        for fingerprint in fingerprints {
991            // Create a minimal pattern from fingerprint
992            let pattern = AttackPattern {
993                id: format!("FP-{:08x}", fingerprint.origin_hash as u32),
994                signature: fingerprint.signature,
995                category: AttackCategory::from_code(fingerprint.category_code),
996                threat_level: fingerprint.level,
997                action_type: crate::proof::ActionType::Custom("mesh-sync".to_string()),
998                target_patterns: Vec::new(),
999                keywords: Vec::new(),
1000                timing_signature: None,
1001                triggered_rule: "mesh-imported".to_string(),
1002                first_seen: fingerprint.timestamp,
1003                occurrence_count: 1,
1004                confidence: 0.7, // Lower initial confidence for mesh-imported
1005            };
1006
1007            self.memory.record(pattern);
1008        }
1009    }
1010
1011    /// Deactivate guard
1012    pub fn deactivate(&self) {
1013        self.active.store(false, Ordering::SeqCst);
1014    }
1015}
1016
1017// ============================================================================
1018// TESTS
1019// ============================================================================
1020
1021#[cfg(test)]
1022mod tests {
1023    use super::*;
1024    use crate::proof::Action;
1025    use crate::watchdog::DenialProof;
1026
1027    fn create_test_denial() -> DenialProof {
1028        // Create a DenialProof directly for testing
1029        let action = Action::read("/etc/passwd - trying to steal passwords");
1030
1031        DenialProof::new(
1032            &action,
1033            "No unauthorized file access".to_string(),
1034            "Attempted to read /etc/passwd - password file access blocked".to_string(),
1035            1,
1036        )
1037    }
1038
1039    #[test]
1040    fn test_attack_pattern_extraction() {
1041        let denial = create_test_denial();
1042        let pattern = AttackPattern::from_denial(&denial, None);
1043
1044        assert!(!pattern.id.is_empty());
1045        // The denial_reason contains "password" not "passwd" as a standalone word
1046        assert!(pattern.keywords.contains(&"password".to_string()));
1047        // passwd path makes it critical threat
1048        assert_eq!(pattern.threat_level, ThreatLevel::Critical);
1049    }
1050
1051    #[test]
1052    fn test_immunity_memory() {
1053        let memory = ImmunityMemory::new();
1054
1055        let denial = create_test_denial();
1056        let pattern = AttackPattern::from_denial(&denial, None);
1057        let signature = pattern.signature;
1058
1059        memory.record(pattern);
1060
1061        assert!(memory.is_known(&signature));
1062        assert_eq!(memory.pattern_count(), 1);
1063        assert_eq!(memory.total_blocked(), 1);
1064    }
1065
1066    #[test]
1067    fn test_filter_generation() {
1068        let denial = create_test_denial();
1069        let pattern = AttackPattern::from_denial(&denial, None);
1070
1071        let generator = FilterGenerator::new().unwrap();
1072        let signed_filter = generator.generate(&[pattern]);
1073
1074        assert!(!signed_filter.filter.rules.is_empty());
1075        assert!(!signed_filter.signature.is_empty());
1076    }
1077
1078    #[test]
1079    fn test_filter_blocks_similar_attack() {
1080        let denial = create_test_denial();
1081        let pattern = AttackPattern::from_denial(&denial, None);
1082
1083        let filter = PolymorphicFilter::from_patterns(&[pattern]);
1084
1085        // Should block similar attack
1086        let result = filter.should_block(&ActionType::Read, "/etc/passwd access attempt");
1087
1088        assert!(result.is_some());
1089    }
1090
1091    #[test]
1092    fn test_polymorphic_mutation() {
1093        let denial = create_test_denial();
1094        let pattern = AttackPattern::from_denial(&denial, None);
1095        let filter = PolymorphicFilter::from_patterns(&[pattern]);
1096
1097        let engine = MutationEngine::new();
1098        let mutated = engine.mutate(&filter);
1099
1100        assert_ne!(filter.id, mutated.id);
1101        assert_eq!(mutated.generation, filter.generation + 1);
1102        assert_eq!(mutated.parent_id, Some(filter.id));
1103    }
1104
1105    #[test]
1106    fn test_evolutionary_guard_learns() {
1107        let guard = EvolutionaryGuard::new().unwrap();
1108
1109        // Process multiple denials
1110        for _ in 0..3 {
1111            let denial = create_test_denial();
1112            guard.learn(&denial);
1113        }
1114
1115        // Should have learned
1116        assert!(guard.memory().pattern_count() >= 1);
1117        assert!(guard.memory().total_blocked() >= 3);
1118    }
1119
1120    #[test]
1121    fn test_system_learns_from_attack() {
1122        let guard = EvolutionaryGuard::new().unwrap();
1123
1124        // First attack - should be recorded
1125        let denial1 = create_test_denial();
1126        let result1 = guard.learn(&denial1);
1127
1128        // Critical threats generate filter immediately
1129        assert!(result1.is_some());
1130
1131        // Guard should now block similar attacks
1132        let block_result = guard.should_block(
1133            &ActionType::Read,
1134            "Reading /etc/passwd for password harvesting",
1135        );
1136
1137        assert!(block_result.is_some());
1138        assert!(block_result.unwrap().contains("Blocked by evolved filter"));
1139    }
1140
1141    #[test]
1142    fn test_evolution_changes_filters() {
1143        let guard = EvolutionaryGuard::new().unwrap();
1144
1145        // Learn from attack
1146        let denial = create_test_denial();
1147        guard.learn(&denial);
1148
1149        let gen_before = guard.generation();
1150
1151        // Evolve
1152        guard.evolve();
1153
1154        let gen_after = guard.generation();
1155
1156        assert!(gen_after > gen_before);
1157    }
1158
1159    #[test]
1160    fn test_mesh_broadcast() {
1161        let guard = EvolutionaryGuard::new().unwrap();
1162
1163        // Learn from critical attack
1164        let denial = create_test_denial();
1165        guard.learn(&denial);
1166
1167        // Should have pending broadcast
1168        let broadcasts = guard.take_pending_broadcasts();
1169        assert!(!broadcasts.is_empty());
1170
1171        // After taking, should be empty
1172        let broadcasts2 = guard.take_pending_broadcasts();
1173        assert!(broadcasts2.is_empty());
1174    }
1175}