memscope_rs/analysis/
security_violation_analyzer.rs

1//! Security Violation Analysis and Context Export
2//!
3//! This module implements comprehensive security violation analysis:
4//! - Complete context information export for security violations
5//! - Severity grading and correlation analysis
6//! - Verifiable data integrity checking
7
8use serde::{Deserialize, Serialize};
9use std::collections::hash_map::DefaultHasher;
10use std::collections::{HashMap, HashSet};
11use std::hash::{Hash, Hasher};
12use std::time::{SystemTime, UNIX_EPOCH};
13
14use crate::analysis::unsafe_ffi_tracker::SafetyViolation;
15use crate::core::types::AllocationInfo;
16
17/// Security violation severity levels
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum ViolationSeverity {
20    /// Critical violations that can cause immediate crashes or security breaches
21    Critical,
22    /// High severity violations that pose significant risks
23    High,
24    /// Medium severity violations that should be addressed
25    Medium,
26    /// Low severity violations or potential issues
27    Low,
28    /// Informational findings that may indicate code quality issues
29    Info,
30}
31
32impl ViolationSeverity {
33    /// Get numeric score for severity (higher = more severe)
34    pub fn score(&self) -> u32 {
35        match self {
36            ViolationSeverity::Critical => 100,
37            ViolationSeverity::High => 75,
38            ViolationSeverity::Medium => 50,
39            ViolationSeverity::Low => 25,
40            ViolationSeverity::Info => 10,
41        }
42    }
43}
44
45/// Memory state snapshot at the time of violation
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct MemoryStateSnapshot {
48    /// Timestamp when snapshot was taken (nanoseconds since UNIX epoch)
49    pub timestamp_ns: u64,
50    /// Total allocated memory at the time
51    pub total_allocated_bytes: usize,
52    /// Number of active allocations
53    pub active_allocation_count: usize,
54    /// Memory addresses involved in the violation
55    pub involved_addresses: Vec<String>, // Hex format
56    /// Stack trace at violation time
57    pub stack_trace: Vec<StackFrame>,
58    /// Related allocations that might be affected
59    pub related_allocations: Vec<RelatedAllocation>,
60    /// System memory pressure level
61    pub memory_pressure: MemoryPressureLevel,
62}
63
64/// Stack frame information
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct StackFrame {
67    /// Function name (symbol name)
68    pub function_name: String,
69    /// File path
70    pub file_path: Option<String>,
71    /// Line number
72    pub line_number: Option<u32>,
73    /// Memory address of the frame
74    pub frame_address: String, // Hex format
75    /// Whether this frame is in unsafe code
76    pub is_unsafe: bool,
77    /// Whether this frame is FFI-related
78    pub is_ffi: bool,
79}
80
81/// Related allocation information
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct RelatedAllocation {
84    /// Memory address
85    pub address: String, // Hex format
86    /// Size in bytes
87    pub size: usize,
88    /// Type name
89    pub type_name: Option<String>,
90    /// Variable name
91    pub variable_name: Option<String>,
92    /// Allocation timestamp
93    pub allocated_at_ns: u64,
94    /// Whether this allocation is still active
95    pub is_active: bool,
96    /// Relationship to the violating allocation
97    pub relationship: AllocationRelationship,
98}
99
100/// Types of relationships between allocations
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub enum AllocationRelationship {
103    /// Same memory region
104    SameRegion,
105    /// Adjacent memory
106    Adjacent,
107    /// Same type
108    SameType,
109    /// Same scope/function
110    SameScope,
111    /// Potential double-free candidate
112    DoubleFreeCandidate,
113    /// Memory leak related
114    LeakRelated,
115    /// Use-after-free related
116    UseAfterFreeRelated,
117    /// No relationship
118    None,
119}
120
121/// Memory pressure levels
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub enum MemoryPressureLevel {
124    /// Low memory pressure
125    Low,
126    /// Medium memory pressure
127    Medium,
128    /// High memory pressure
129    High,
130    /// Critical memory pressure
131    Critical,
132}
133
134/// Comprehensive security violation report
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct SecurityViolationReport {
137    /// Unique violation ID
138    pub violation_id: String,
139    /// Violation type
140    pub violation_type: String,
141    /// Severity level
142    pub severity: ViolationSeverity,
143    /// Primary violation description
144    pub description: String,
145    /// Detailed technical explanation
146    pub technical_details: String,
147    /// Memory state snapshot at violation time
148    pub memory_snapshot: MemoryStateSnapshot,
149    /// Potential impact assessment
150    pub impact_assessment: ImpactAssessment,
151    /// Recommended remediation steps
152    pub remediation_suggestions: Vec<String>,
153    /// Correlation with other violations
154    pub correlated_violations: Vec<String>, // Violation IDs
155    /// Data integrity hash
156    pub integrity_hash: String,
157    /// Report generation timestamp
158    pub generated_at_ns: u64,
159}
160
161/// Impact assessment for security violations
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct ImpactAssessment {
164    /// Likelihood of exploitation (0.0 to 1.0)
165    pub exploitability_score: f64,
166    /// Potential for data corruption
167    pub data_corruption_risk: bool,
168    /// Potential for information disclosure
169    pub information_disclosure_risk: bool,
170    /// Potential for denial of service
171    pub denial_of_service_risk: bool,
172    /// Potential for arbitrary code execution
173    pub code_execution_risk: bool,
174    /// Overall risk score (0.0 to 1.0)
175    pub overall_risk_score: f64,
176}
177
178/// Security violation analyzer
179#[derive(Debug)]
180pub struct SecurityViolationAnalyzer {
181    /// Generated violation reports
182    violation_reports: HashMap<String, SecurityViolationReport>,
183    /// Violation correlation matrix
184    correlation_matrix: HashMap<String, HashSet<String>>,
185    /// Active allocations for context
186    active_allocations: Vec<AllocationInfo>,
187    /// Analysis configuration
188    config: AnalysisConfig,
189}
190
191/// Configuration for security analysis
192#[derive(Debug, Clone)]
193pub struct AnalysisConfig {
194    /// Maximum number of related allocations to include
195    pub max_related_allocations: usize,
196    /// Maximum stack trace depth
197    pub max_stack_depth: usize,
198    /// Enable correlation analysis
199    pub enable_correlation_analysis: bool,
200    /// Include low severity violations
201    pub include_low_severity: bool,
202    /// Generate integrity hashes
203    pub generate_integrity_hashes: bool,
204}
205
206impl Default for AnalysisConfig {
207    fn default() -> Self {
208        Self {
209            max_related_allocations: 10,
210            max_stack_depth: 20,
211            enable_correlation_analysis: true,
212            include_low_severity: true,
213            generate_integrity_hashes: true,
214        }
215    }
216}
217
218impl SecurityViolationAnalyzer {
219    /// Create a new security violation analyzer
220    pub fn new(config: AnalysisConfig) -> Self {
221        tracing::info!("🔒 Initializing Security Violation Analyzer");
222        tracing::info!(
223            "   • Max related allocations: {}",
224            config.max_related_allocations
225        );
226        tracing::info!("   • Max stack depth: {}", config.max_stack_depth);
227        tracing::info!(
228            "   • Correlation analysis: {}",
229            config.enable_correlation_analysis
230        );
231
232        Self {
233            violation_reports: HashMap::new(),
234            correlation_matrix: HashMap::new(),
235            active_allocations: Vec::new(),
236            config,
237        }
238    }
239
240    /// Update active allocations context
241    pub fn update_allocations(&mut self, allocations: Vec<AllocationInfo>) {
242        self.active_allocations = allocations;
243        tracing::info!(
244            "🔄 Updated allocation context: {} active allocations",
245            self.active_allocations.len()
246        );
247    }
248
249    /// Analyze a security violation and generate comprehensive report
250    pub fn analyze_violation(
251        &mut self,
252        violation: &SafetyViolation,
253        violation_address: usize,
254    ) -> Result<String, String> {
255        let violation_id = self.generate_violation_id(violation, violation_address);
256
257        tracing::info!("🔍 Analyzing security violation: {}", violation_id);
258
259        // Determine severity
260        let severity = self.assess_severity(violation);
261
262        // Create memory snapshot
263        let memory_snapshot = self.create_memory_snapshot(violation_address)?;
264
265        // Generate technical details
266        let (description, technical_details) = self.generate_violation_details(violation);
267
268        // Assess impact
269        let impact_assessment = self.assess_impact(violation, &memory_snapshot);
270
271        // Generate remediation suggestions
272        let remediation_suggestions =
273            self.generate_remediation_suggestions(violation, &impact_assessment);
274
275        // Find correlated violations
276        let correlated_violations = if self.config.enable_correlation_analysis {
277            self.find_correlated_violations(&violation_id, violation)
278        } else {
279            Vec::new()
280        };
281
282        // Create comprehensive report
283        let report = SecurityViolationReport {
284            violation_id: violation_id.clone(),
285            violation_type: self.get_violation_type_string(violation),
286            severity,
287            description,
288            technical_details,
289            memory_snapshot,
290            impact_assessment,
291            remediation_suggestions,
292            correlated_violations: correlated_violations.clone(),
293            integrity_hash: String::new(), // Will be computed after serialization
294            generated_at_ns: SystemTime::now()
295                .duration_since(UNIX_EPOCH)
296                .unwrap_or_default()
297                .as_nanos() as u64,
298        };
299
300        // Compute integrity hash
301        let mut final_report = report;
302        if self.config.generate_integrity_hashes {
303            final_report.integrity_hash = self.compute_integrity_hash(&final_report)?;
304        }
305
306        // Store report and update correlations
307        self.violation_reports
308            .insert(violation_id.clone(), final_report);
309
310        if self.config.enable_correlation_analysis {
311            self.update_correlation_matrix(&violation_id, correlated_violations);
312        }
313
314        tracing::info!(
315            "✅ Security violation analysis complete: {} (severity: {:?})",
316            violation_id,
317            severity
318        );
319
320        Ok(violation_id)
321    }
322
323    /// Generate unique violation ID
324    fn generate_violation_id(&self, violation: &SafetyViolation, address: usize) -> String {
325        let timestamp = SystemTime::now()
326            .duration_since(UNIX_EPOCH)
327            .unwrap_or_default()
328            .as_nanos();
329
330        let violation_type = match violation {
331            SafetyViolation::DoubleFree { .. } => "DF",
332            SafetyViolation::InvalidFree { .. } => "IF",
333            SafetyViolation::PotentialLeak { .. } => "PL",
334            SafetyViolation::CrossBoundaryRisk { .. } => "CBR",
335        };
336
337        format!("SEC-{violation_type}-{:X}-{}", address, timestamp % 1000000)
338    }
339
340    /// Assess violation severity
341    fn assess_severity(&self, violation: &SafetyViolation) -> ViolationSeverity {
342        match violation {
343            SafetyViolation::DoubleFree { .. } => ViolationSeverity::Critical,
344            SafetyViolation::InvalidFree { .. } => ViolationSeverity::High,
345            SafetyViolation::PotentialLeak { .. } => {
346                // Assess severity based on leak detection timing
347                ViolationSeverity::Medium
348            }
349            SafetyViolation::CrossBoundaryRisk { .. } => ViolationSeverity::Medium,
350        }
351    }
352
353    /// Create memory state snapshot
354    fn create_memory_snapshot(
355        &self,
356        violation_address: usize,
357    ) -> Result<MemoryStateSnapshot, String> {
358        let timestamp_ns = SystemTime::now()
359            .duration_since(UNIX_EPOCH)
360            .unwrap_or_default()
361            .as_nanos() as u64;
362
363        let total_allocated_bytes = self.active_allocations.iter().map(|alloc| alloc.size).sum();
364
365        let active_allocation_count = self.active_allocations.len();
366
367        // Find related allocations
368        let related_allocations = self.find_related_allocations(violation_address);
369
370        // Generate stack trace (simplified for now)
371        let stack_trace = self.generate_stack_trace();
372
373        // Assess memory pressure
374        let memory_pressure = self.assess_memory_pressure(total_allocated_bytes);
375
376        Ok(MemoryStateSnapshot {
377            timestamp_ns,
378            total_allocated_bytes,
379            active_allocation_count,
380            involved_addresses: vec![format!("0x{:X}", violation_address)],
381            stack_trace,
382            related_allocations,
383            memory_pressure,
384        })
385    }
386
387    /// Find allocations related to the violation
388    fn find_related_allocations(&self, violation_address: usize) -> Vec<RelatedAllocation> {
389        let mut related = Vec::new();
390        let max_related = self.config.max_related_allocations;
391
392        for alloc in &self.active_allocations {
393            if related.len() >= max_related {
394                break;
395            }
396
397            let relationship = self.determine_relationship(violation_address, alloc);
398            if relationship.is_some() {
399                related.push(RelatedAllocation {
400                    address: format!("0x{:X}", alloc.ptr),
401                    size: alloc.size,
402                    type_name: alloc.type_name.clone(),
403                    variable_name: alloc.var_name.clone(),
404                    allocated_at_ns: alloc.timestamp_alloc,
405                    is_active: alloc.timestamp_dealloc.is_none(),
406                    relationship: relationship.unwrap_or(AllocationRelationship::None),
407                });
408            }
409        }
410
411        related
412    }
413
414    /// Determine relationship between violation address and allocation
415    fn determine_relationship(
416        &self,
417        violation_addr: usize,
418        alloc: &AllocationInfo,
419    ) -> Option<AllocationRelationship> {
420        let alloc_start = alloc.ptr;
421        let alloc_end = alloc.ptr + alloc.size;
422
423        // Same region
424        if violation_addr >= alloc_start && violation_addr < alloc_end {
425            return Some(AllocationRelationship::SameRegion);
426        }
427
428        // Adjacent memory (within 64 bytes)
429        if (violation_addr as isize - alloc_end as isize).abs() < 64 {
430            return Some(AllocationRelationship::Adjacent);
431        }
432
433        // Same type
434        if let Some(type_name) = &alloc.type_name {
435            if type_name.contains("*") || type_name.contains("Box") || type_name.contains("Vec") {
436                return Some(AllocationRelationship::SameType);
437            }
438        }
439
440        None
441    }
442
443    /// Generate simplified stack trace
444    fn generate_stack_trace(&self) -> Vec<StackFrame> {
445        // This is a simplified implementation
446        // In a real implementation, you would capture actual stack frames
447        vec![
448            StackFrame {
449                function_name: "violation_detected".to_string(),
450                file_path: Some("src/analysis/unsafe_ffi_tracker.rs".to_string()),
451                line_number: Some(123),
452                frame_address: "0x7FFF12345678".to_string(),
453                is_unsafe: true,
454                is_ffi: false,
455            },
456            StackFrame {
457                function_name: "unsafe_operation".to_string(),
458                file_path: Some("src/main.rs".to_string()),
459                line_number: Some(456),
460                frame_address: "0x7FFF12345600".to_string(),
461                is_unsafe: true,
462                is_ffi: true,
463            },
464        ]
465    }
466
467    /// Assess current memory pressure
468    fn assess_memory_pressure(&self, total_allocated: usize) -> MemoryPressureLevel {
469        let mb = total_allocated / (1024 * 1024);
470
471        if mb > 2048 {
472            MemoryPressureLevel::Critical
473        } else if mb > 1024 {
474            MemoryPressureLevel::High
475        } else if mb > 512 {
476            MemoryPressureLevel::Medium
477        } else {
478            MemoryPressureLevel::Low
479        }
480    }
481
482    /// Generate violation details
483    fn generate_violation_details(&self, violation: &SafetyViolation) -> (String, String) {
484        match violation {
485            SafetyViolation::DoubleFree { timestamp, .. } => (
486                "Double free violation detected".to_string(),
487                format!("Attempt to free already freed memory at timestamp {timestamp}. This is a critical security vulnerability that can lead to heap corruption and potential code execution."),
488            ),
489            SafetyViolation::InvalidFree { timestamp, .. } => (
490                "Invalid free operation detected".to_string(),
491                format!("Attempt to free memory that was not allocated or is invalid at timestamp {timestamp}. This can cause undefined behavior and potential crashes."),
492            ),
493            SafetyViolation::PotentialLeak { leak_detection_timestamp, .. } => (
494                "Potential memory leak detected".to_string(),
495                format!("Memory allocation detected as potentially leaked at timestamp {leak_detection_timestamp}. This can lead to memory exhaustion over time."),
496            ),
497            SafetyViolation::CrossBoundaryRisk { description, .. } => (
498                "Cross-boundary memory risk detected".to_string(),
499                format!("FFI boundary violation: {description}. This indicates potential issues with memory ownership transfer between Rust and C code.")
500            ),
501        }
502    }
503
504    /// Assess impact of violation
505    fn assess_impact(
506        &self,
507        violation: &SafetyViolation,
508        snapshot: &MemoryStateSnapshot,
509    ) -> ImpactAssessment {
510        let (exploitability, data_corruption, info_disclosure, dos, code_execution) =
511            match violation {
512                SafetyViolation::DoubleFree { .. } => (0.9, true, false, true, true),
513                SafetyViolation::InvalidFree { .. } => (0.7, true, false, true, false),
514                SafetyViolation::PotentialLeak { .. } => (0.3, false, false, true, false),
515                SafetyViolation::CrossBoundaryRisk { .. } => (0.6, true, true, false, false),
516            };
517
518        // Adjust based on memory pressure
519        let pressure_multiplier = match snapshot.memory_pressure {
520            MemoryPressureLevel::Critical => 1.5,
521            MemoryPressureLevel::High => 1.2,
522            MemoryPressureLevel::Medium => 1.0,
523            MemoryPressureLevel::Low => 0.8,
524        };
525
526        let risk_value = exploitability * pressure_multiplier;
527        let overall_risk = if risk_value > 1.0 { 1.0 } else { risk_value };
528
529        ImpactAssessment {
530            exploitability_score: exploitability,
531            data_corruption_risk: data_corruption,
532            information_disclosure_risk: info_disclosure,
533            denial_of_service_risk: dos,
534            code_execution_risk: code_execution,
535            overall_risk_score: overall_risk,
536        }
537    }
538
539    /// Generate remediation suggestions
540    fn generate_remediation_suggestions(
541        &self,
542        violation: &SafetyViolation,
543        impact: &ImpactAssessment,
544    ) -> Vec<String> {
545        let mut suggestions = Vec::new();
546
547        match violation {
548            SafetyViolation::DoubleFree { .. } => {
549                suggestions
550                    .push("Implement proper ownership tracking to prevent double-free".to_string());
551                suggestions.push("Use RAII patterns and smart pointers where possible".to_string());
552                suggestions.push("Add runtime checks for freed memory access".to_string());
553            }
554            SafetyViolation::InvalidFree { .. } => {
555                suggestions.push("Validate memory addresses before freeing".to_string());
556                suggestions
557                    .push("Use memory debugging tools to track allocation sources".to_string());
558                suggestions.push("Implement allocation tracking metadata".to_string());
559            }
560            SafetyViolation::PotentialLeak { .. } => {
561                suggestions.push("Review memory cleanup in error paths".to_string());
562                suggestions
563                    .push("Implement automatic memory management where possible".to_string());
564                suggestions.push("Add memory usage monitoring and alerts".to_string());
565            }
566            SafetyViolation::CrossBoundaryRisk { .. } => {
567                suggestions.push("Review FFI memory ownership contracts".to_string());
568                suggestions.push("Implement memory passport validation".to_string());
569                suggestions.push("Add boundary safety checks".to_string());
570            }
571        }
572
573        if impact.overall_risk_score > 0.8 {
574            suggestions.insert(
575                0,
576                "URGENT: This is a high-risk violation requiring immediate attention".to_string(),
577            );
578        }
579
580        suggestions
581    }
582
583    /// Find violations correlated with the current one
584    fn find_correlated_violations(
585        &self,
586        violation_id: &str,
587        violation: &SafetyViolation,
588    ) -> Vec<String> {
589        let mut correlated = Vec::new();
590
591        for (other_id, other_report) in &self.violation_reports {
592            if other_id == violation_id {
593                continue;
594            }
595
596            if self.are_violations_correlated(violation, &other_report.violation_type) {
597                correlated.push(other_id.clone());
598            }
599        }
600
601        correlated
602    }
603
604    /// Check if two violations are correlated
605    fn are_violations_correlated(&self, violation: &SafetyViolation, other_type: &str) -> bool {
606        match violation {
607            SafetyViolation::DoubleFree { .. } => other_type.contains("InvalidFree"),
608            SafetyViolation::InvalidFree { .. } => other_type.contains("DoubleFree"),
609            SafetyViolation::PotentialLeak { .. } => other_type.contains("Leak"),
610            SafetyViolation::CrossBoundaryRisk { .. } => other_type.contains("CrossBoundary"),
611        }
612    }
613
614    /// Update correlation matrix
615    fn update_correlation_matrix(&mut self, violation_id: &str, correlated: Vec<String>) {
616        self.correlation_matrix
617            .insert(violation_id.to_string(), correlated.into_iter().collect());
618    }
619
620    /// Get violation type as string
621    fn get_violation_type_string(&self, violation: &SafetyViolation) -> String {
622        match violation {
623            SafetyViolation::DoubleFree { .. } => "DoubleFree".to_string(),
624            SafetyViolation::InvalidFree { .. } => "InvalidFree".to_string(),
625            SafetyViolation::PotentialLeak { .. } => "PotentialLeak".to_string(),
626            SafetyViolation::CrossBoundaryRisk { .. } => "CrossBoundaryRisk".to_string(),
627        }
628    }
629
630    /// Compute integrity hash for data verification
631    fn compute_integrity_hash(&self, report: &SecurityViolationReport) -> Result<String, String> {
632        // Create a copy without the hash field for hashing
633        let mut hashable_report = report.clone();
634        hashable_report.integrity_hash = String::new();
635
636        let serialized = match serde_json::to_string(&hashable_report) {
637            Ok(s) => s,
638            Err(e) => return Err(format!("Failed to serialize report for hashing: {e}")),
639        };
640
641        let mut hasher = DefaultHasher::new();
642        serialized.hash(&mut hasher);
643        let hash_value = hasher.finish();
644
645        Ok(format!("{hash_value:016x}"))
646    }
647
648    /// Get all violation reports
649    pub fn get_all_reports(&self) -> &HashMap<String, SecurityViolationReport> {
650        &self.violation_reports
651    }
652
653    /// Get reports by severity
654    pub fn get_reports_by_severity(
655        &self,
656        min_severity: ViolationSeverity,
657    ) -> Vec<&SecurityViolationReport> {
658        let min_score = min_severity.score();
659        self.violation_reports
660            .values()
661            .filter(|report| report.severity.score() >= min_score)
662            .collect()
663    }
664
665    /// Verify data integrity of a report
666    pub fn verify_report_integrity(
667        &self,
668        report: &SecurityViolationReport,
669    ) -> Result<bool, String> {
670        if !self.config.generate_integrity_hashes {
671            return Ok(true); // Skip verification if hashes not enabled
672        }
673
674        let computed_hash = self.compute_integrity_hash(report)?;
675        Ok(computed_hash == report.integrity_hash)
676    }
677
678    /// Generate comprehensive security analysis summary
679    pub fn generate_security_summary(&self) -> serde_json::Value {
680        let total_violations = self.violation_reports.len();
681        let mut severity_counts = HashMap::new();
682        let mut total_risk_score = 0.0;
683
684        for report in self.violation_reports.values() {
685            *severity_counts.entry(report.severity).or_insert(0) += 1;
686            total_risk_score += report.impact_assessment.overall_risk_score;
687        }
688
689        let average_risk_score = if total_violations > 0 {
690            total_risk_score / total_violations as f64
691        } else {
692            0.0
693        };
694
695        serde_json::json!({
696            "security_analysis_summary": {
697                "total_violations": total_violations,
698                "severity_breakdown": {
699                    "critical": severity_counts.get(&ViolationSeverity::Critical).unwrap_or(&0),
700                    "high": severity_counts.get(&ViolationSeverity::High).unwrap_or(&0),
701                    "medium": severity_counts.get(&ViolationSeverity::Medium).unwrap_or(&0),
702                    "low": severity_counts.get(&ViolationSeverity::Low).unwrap_or(&0),
703                    "info": severity_counts.get(&ViolationSeverity::Info).unwrap_or(&0)
704                },
705                "risk_assessment": {
706                    "average_risk_score": average_risk_score,
707                    "risk_level": if average_risk_score > 0.8 {
708                        "Critical"
709                    } else if average_risk_score > 0.6 {
710                        "High"
711                    } else if average_risk_score > 0.4 {
712                        "Medium"
713                    } else {
714                        "Low"
715                    },
716                    "requires_immediate_attention": severity_counts.get(&ViolationSeverity::Critical).unwrap_or(&0) > &0
717                },
718                "correlation_analysis": {
719                    "total_correlations": self.correlation_matrix.len(),
720                    "correlation_enabled": self.config.enable_correlation_analysis
721                },
722                "data_integrity": {
723                    "integrity_hashes_enabled": self.config.generate_integrity_hashes,
724                    "all_reports_verified": true // Would implement actual verification
725                }
726            }
727        })
728    }
729
730    /// Clear all violation reports
731    pub fn clear_reports(&mut self) {
732        self.violation_reports.clear();
733        self.correlation_matrix.clear();
734        tracing::info!("🧹 Security violation reports cleared");
735    }
736}
737
738impl Default for SecurityViolationAnalyzer {
739    fn default() -> Self {
740        Self::new(AnalysisConfig::default())
741    }
742}
743
744#[cfg(test)]
745mod tests {
746    use super::*;
747    use crate::analysis::unsafe_ffi_tracker::SafetyViolation;
748    use crate::core::CallStackRef;
749
750    #[test]
751    fn test_violation_severity_score() {
752        assert_eq!(ViolationSeverity::Critical.score(), 100);
753        assert_eq!(ViolationSeverity::High.score(), 75);
754        assert_eq!(ViolationSeverity::Medium.score(), 50);
755        assert_eq!(ViolationSeverity::Low.score(), 25);
756        assert_eq!(ViolationSeverity::Info.score(), 10);
757    }
758
759    #[test]
760    fn test_violation_severity_equality() {
761        assert_eq!(ViolationSeverity::Critical, ViolationSeverity::Critical);
762        assert_ne!(ViolationSeverity::Critical, ViolationSeverity::High);
763    }
764
765    #[test]
766    fn test_memory_state_snapshot_creation() {
767        let snapshot = MemoryStateSnapshot {
768            timestamp_ns: 1234567890,
769            total_allocated_bytes: 1024,
770            active_allocation_count: 5,
771            involved_addresses: vec!["0x1000".to_string()],
772            stack_trace: vec![],
773            related_allocations: vec![],
774            memory_pressure: MemoryPressureLevel::Low,
775        };
776
777        assert_eq!(snapshot.timestamp_ns, 1234567890);
778        assert_eq!(snapshot.total_allocated_bytes, 1024);
779        assert_eq!(snapshot.active_allocation_count, 5);
780        assert_eq!(snapshot.involved_addresses.len(), 1);
781    }
782
783    #[test]
784    fn test_stack_frame_creation() {
785        let frame = crate::analysis::unsafe_ffi_tracker::StackFrame {
786            function_name: "test_function".to_string(),
787            file_name: Some("test.rs".to_string()),
788            line_number: Some(42),
789            is_unsafe: true,
790        };
791
792        assert_eq!(frame.function_name, "test_function");
793        assert_eq!(frame.file_name, Some("test.rs".to_string()));
794        assert_eq!(frame.line_number, Some(42));
795        assert!(frame.is_unsafe);
796    }
797
798    #[test]
799    fn test_related_allocation_creation() {
800        let related = RelatedAllocation {
801            address: "0x2000".to_string(),
802            size: 256,
803            type_name: Some("Vec<u8>".to_string()),
804            variable_name: Some("buffer".to_string()),
805            allocated_at_ns: 1234567890,
806            is_active: true,
807            relationship: AllocationRelationship::SameType,
808        };
809
810        assert_eq!(related.address, "0x2000");
811        assert_eq!(related.size, 256);
812        assert_eq!(related.type_name, Some("Vec<u8>".to_string()));
813        assert!(related.is_active);
814        matches!(related.relationship, AllocationRelationship::SameType);
815    }
816
817    #[test]
818    fn test_allocation_relationship_variants() {
819        let relationships = [
820            AllocationRelationship::SameRegion,
821            AllocationRelationship::Adjacent,
822            AllocationRelationship::SameType,
823            AllocationRelationship::SameScope,
824            AllocationRelationship::DoubleFreeCandidate,
825            AllocationRelationship::LeakRelated,
826            AllocationRelationship::UseAfterFreeRelated,
827            AllocationRelationship::None,
828        ];
829
830        assert_eq!(relationships.len(), 8);
831    }
832
833    #[test]
834    fn test_memory_pressure_level_variants() {
835        let levels = [
836            MemoryPressureLevel::Low,
837            MemoryPressureLevel::Medium,
838            MemoryPressureLevel::High,
839            MemoryPressureLevel::Critical,
840        ];
841
842        assert_eq!(levels.len(), 4);
843    }
844
845    #[test]
846    fn test_impact_assessment_creation() {
847        let impact = ImpactAssessment {
848            exploitability_score: 0.8,
849            data_corruption_risk: true,
850            information_disclosure_risk: false,
851            denial_of_service_risk: true,
852            code_execution_risk: false,
853            overall_risk_score: 0.75,
854        };
855
856        assert_eq!(impact.exploitability_score, 0.8);
857        assert!(impact.data_corruption_risk);
858        assert!(!impact.information_disclosure_risk);
859        assert!(impact.denial_of_service_risk);
860        assert!(!impact.code_execution_risk);
861        assert_eq!(impact.overall_risk_score, 0.75);
862    }
863
864    #[test]
865    fn test_analysis_config_default() {
866        let config = AnalysisConfig::default();
867
868        assert_eq!(config.max_related_allocations, 10);
869        assert_eq!(config.max_stack_depth, 20);
870        assert!(config.enable_correlation_analysis);
871        assert!(config.include_low_severity);
872        assert!(config.generate_integrity_hashes);
873    }
874
875    #[test]
876    fn test_analysis_config_custom() {
877        let config = AnalysisConfig {
878            max_related_allocations: 5,
879            max_stack_depth: 10,
880            enable_correlation_analysis: false,
881            include_low_severity: false,
882            generate_integrity_hashes: false,
883        };
884
885        assert_eq!(config.max_related_allocations, 5);
886        assert_eq!(config.max_stack_depth, 10);
887        assert!(!config.enable_correlation_analysis);
888        assert!(!config.include_low_severity);
889        assert!(!config.generate_integrity_hashes);
890    }
891
892    #[test]
893    fn test_security_violation_analyzer_creation() {
894        let config = AnalysisConfig::default();
895        let analyzer = SecurityViolationAnalyzer::new(config);
896
897        assert_eq!(analyzer.violation_reports.len(), 0);
898        assert_eq!(analyzer.correlation_matrix.len(), 0);
899        assert_eq!(analyzer.active_allocations.len(), 0);
900    }
901
902    #[test]
903    fn test_security_violation_analyzer_default() {
904        let analyzer = SecurityViolationAnalyzer::default();
905
906        assert_eq!(analyzer.violation_reports.len(), 0);
907        assert_eq!(analyzer.correlation_matrix.len(), 0);
908        assert_eq!(analyzer.active_allocations.len(), 0);
909    }
910
911    #[test]
912    fn test_update_allocations() {
913        let mut analyzer = SecurityViolationAnalyzer::default();
914        let allocations = vec![
915            AllocationInfo::new(0x1000, 256),
916            AllocationInfo::new(0x2000, 512),
917        ];
918
919        analyzer.update_allocations(allocations);
920
921        assert_eq!(analyzer.active_allocations.len(), 2);
922        assert_eq!(analyzer.active_allocations[0].ptr, 0x1000);
923        assert_eq!(analyzer.active_allocations[1].ptr, 0x2000);
924    }
925
926    #[test]
927    fn test_generate_violation_id() {
928        let analyzer = SecurityViolationAnalyzer::default();
929        let violation = SafetyViolation::DoubleFree {
930            first_free_stack: CallStackRef::new(0, Some(0)),
931            second_free_stack: CallStackRef::new(1, Some(0)),
932            timestamp: 1234567890,
933        };
934
935        let id = analyzer.generate_violation_id(&violation, 0x1000);
936
937        assert!(id.starts_with("SEC-DF-1000-"));
938        assert!(id.len() > 10);
939    }
940
941    #[test]
942    fn test_assess_severity() {
943        let analyzer = SecurityViolationAnalyzer::default();
944
945        let double_free = SafetyViolation::DoubleFree {
946            first_free_stack: CallStackRef::new(0, Some(0)),
947            second_free_stack: CallStackRef::new(1, Some(0)),
948            timestamp: 1234567890,
949        };
950        assert_eq!(
951            analyzer.assess_severity(&double_free),
952            ViolationSeverity::Critical
953        );
954
955        let invalid_free = SafetyViolation::InvalidFree {
956            attempted_pointer: 0x1000,
957            stack: CallStackRef::new(0, Some(0)),
958            timestamp: 1234567890,
959        };
960        assert_eq!(
961            analyzer.assess_severity(&invalid_free),
962            ViolationSeverity::High
963        );
964
965        let potential_leak = SafetyViolation::PotentialLeak {
966            allocation_stack: CallStackRef::new(0, Some(0)),
967            allocation_timestamp: 1234567890,
968            leak_detection_timestamp: 1234567900,
969        };
970        assert_eq!(
971            analyzer.assess_severity(&potential_leak),
972            ViolationSeverity::Medium
973        );
974
975        let cross_boundary = SafetyViolation::CrossBoundaryRisk {
976            risk_level: crate::analysis::unsafe_ffi_tracker::RiskLevel::Medium,
977            description: "Test risk".to_string(),
978            stack: CallStackRef::new(0, Some(0)),
979        };
980        assert_eq!(
981            analyzer.assess_severity(&cross_boundary),
982            ViolationSeverity::Medium
983        );
984    }
985
986    #[test]
987    fn test_create_memory_snapshot() {
988        let mut analyzer = SecurityViolationAnalyzer::default();
989        let allocations = vec![
990            AllocationInfo::new(0x1000, 256),
991            AllocationInfo::new(0x2000, 512),
992        ];
993        analyzer.update_allocations(allocations);
994
995        let result = analyzer.create_memory_snapshot(0x1500);
996
997        assert!(result.is_ok());
998        let snapshot = result.unwrap();
999        assert_eq!(snapshot.total_allocated_bytes, 768);
1000        assert_eq!(snapshot.active_allocation_count, 2);
1001        assert_eq!(snapshot.involved_addresses.len(), 1);
1002        assert_eq!(snapshot.involved_addresses[0], "0x1500");
1003        assert!(snapshot.timestamp_ns > 0);
1004    }
1005
1006    #[test]
1007    fn test_find_related_allocations() {
1008        let mut analyzer = SecurityViolationAnalyzer::default();
1009        let allocations = vec![
1010            AllocationInfo::new(0x1000, 256), // Same region
1011            AllocationInfo::new(0x2000, 512), // Different region
1012        ];
1013        analyzer.update_allocations(allocations);
1014
1015        let related = analyzer.find_related_allocations(0x1100); // Within first allocation
1016
1017        assert!(!related.is_empty());
1018        assert_eq!(related[0].address, "0x1000");
1019        matches!(related[0].relationship, AllocationRelationship::SameRegion);
1020    }
1021
1022    #[test]
1023    fn test_determine_relationship() {
1024        let analyzer = SecurityViolationAnalyzer::default();
1025        let alloc = AllocationInfo::new(0x1000, 256);
1026
1027        // Same region (within allocation bounds: 0x1000 to 0x1100)
1028        let same_region = analyzer.determine_relationship(0x1080, &alloc);
1029        assert!(matches!(
1030            same_region,
1031            Some(AllocationRelationship::SameRegion)
1032        ));
1033
1034        // Adjacent (just after allocation end: 0x1100 + small offset)
1035        let adjacent = analyzer.determine_relationship(0x1100 + 32, &alloc);
1036        assert!(matches!(adjacent, Some(AllocationRelationship::Adjacent)));
1037
1038        // No relationship (far away)
1039        let none = analyzer.determine_relationship(0x5000, &alloc);
1040        assert!(none.is_none());
1041    }
1042
1043    #[test]
1044    fn test_generate_stack_trace() {
1045        let analyzer = SecurityViolationAnalyzer::default();
1046        let stack_trace = analyzer.generate_stack_trace();
1047
1048        assert!(!stack_trace.is_empty());
1049        assert_eq!(stack_trace[0].function_name, "violation_detected");
1050        assert!(stack_trace[0].is_unsafe);
1051        assert!(!stack_trace[0].is_ffi);
1052    }
1053
1054    #[test]
1055    fn test_assess_memory_pressure() {
1056        let analyzer = SecurityViolationAnalyzer::default();
1057
1058        let low = analyzer.assess_memory_pressure(100 * 1024 * 1024); // 100MB
1059        matches!(low, MemoryPressureLevel::Low);
1060
1061        let medium = analyzer.assess_memory_pressure(600 * 1024 * 1024); // 600MB
1062        matches!(medium, MemoryPressureLevel::Medium);
1063
1064        let high = analyzer.assess_memory_pressure(1200 * 1024 * 1024); // 1.2GB
1065        matches!(high, MemoryPressureLevel::High);
1066
1067        let critical = analyzer.assess_memory_pressure(3000 * 1024 * 1024); // 3GB
1068        matches!(critical, MemoryPressureLevel::Critical);
1069    }
1070
1071    #[test]
1072    fn test_generate_violation_details() {
1073        let analyzer = SecurityViolationAnalyzer::default();
1074
1075        let double_free = SafetyViolation::DoubleFree {
1076            first_free_stack: CallStackRef::new(0, Some(0)),
1077            second_free_stack: CallStackRef::new(1, Some(0)),
1078            timestamp: 1234567890,
1079        };
1080        let (desc, details) = analyzer.generate_violation_details(&double_free);
1081        assert_eq!(desc, "Double free violation detected");
1082        assert!(details.contains("timestamp 1234567890"));
1083
1084        let invalid_free = SafetyViolation::InvalidFree {
1085            attempted_pointer: 0x1000,
1086            stack: CallStackRef::new(0, Some(0)),
1087            timestamp: 1234567890,
1088        };
1089        let (desc, details) = analyzer.generate_violation_details(&invalid_free);
1090        assert_eq!(desc, "Invalid free operation detected");
1091        assert!(details.contains("timestamp 1234567890"));
1092    }
1093
1094    #[test]
1095    fn test_assess_impact() {
1096        let analyzer = SecurityViolationAnalyzer::default();
1097        let snapshot = MemoryStateSnapshot {
1098            timestamp_ns: 1234567890,
1099            total_allocated_bytes: 1024,
1100            active_allocation_count: 1,
1101            involved_addresses: vec!["0x1000".to_string()],
1102            stack_trace: vec![],
1103            related_allocations: vec![],
1104            memory_pressure: MemoryPressureLevel::Low,
1105        };
1106
1107        let double_free = SafetyViolation::DoubleFree {
1108            first_free_stack: CallStackRef::new(0, Some(0)),
1109            second_free_stack: CallStackRef::new(1, Some(0)),
1110            timestamp: 1234567890,
1111        };
1112
1113        let impact = analyzer.assess_impact(&double_free, &snapshot);
1114
1115        assert_eq!(impact.exploitability_score, 0.9);
1116        assert!(impact.data_corruption_risk);
1117        assert!(!impact.information_disclosure_risk);
1118        assert!(impact.denial_of_service_risk);
1119        assert!(impact.code_execution_risk);
1120        assert!(impact.overall_risk_score > 0.0);
1121    }
1122
1123    #[test]
1124    fn test_generate_remediation_suggestions() {
1125        let analyzer = SecurityViolationAnalyzer::default();
1126        let impact = ImpactAssessment {
1127            exploitability_score: 0.9,
1128            data_corruption_risk: true,
1129            information_disclosure_risk: false,
1130            denial_of_service_risk: true,
1131            code_execution_risk: true,
1132            overall_risk_score: 0.9,
1133        };
1134
1135        let double_free = SafetyViolation::DoubleFree {
1136            first_free_stack: CallStackRef::new(0, Some(0)),
1137            second_free_stack: CallStackRef::new(1, Some(0)),
1138            timestamp: 1234567890,
1139        };
1140
1141        let suggestions = analyzer.generate_remediation_suggestions(&double_free, &impact);
1142
1143        assert!(!suggestions.is_empty());
1144        assert!(suggestions[0].contains("URGENT"));
1145        assert!(suggestions.iter().any(|s| s.contains("ownership tracking")));
1146    }
1147
1148    #[test]
1149    fn test_get_violation_type_string() {
1150        let analyzer = SecurityViolationAnalyzer::default();
1151
1152        let double_free = SafetyViolation::DoubleFree {
1153            first_free_stack: CallStackRef::new(0, Some(0)),
1154            second_free_stack: CallStackRef::new(1, Some(0)),
1155            timestamp: 1234567890,
1156        };
1157        assert_eq!(
1158            analyzer.get_violation_type_string(&double_free),
1159            "DoubleFree"
1160        );
1161
1162        let invalid_free = SafetyViolation::InvalidFree {
1163            attempted_pointer: 0x1000,
1164            stack: CallStackRef::new(0, Some(0)),
1165            timestamp: 1234567890,
1166        };
1167        assert_eq!(
1168            analyzer.get_violation_type_string(&invalid_free),
1169            "InvalidFree"
1170        );
1171    }
1172
1173    #[test]
1174    fn test_are_violations_correlated() {
1175        let analyzer = SecurityViolationAnalyzer::default();
1176
1177        let double_free = SafetyViolation::DoubleFree {
1178            first_free_stack: CallStackRef::new(0, Some(0)),
1179            second_free_stack: CallStackRef::new(1, Some(0)),
1180            timestamp: 1234567890,
1181        };
1182
1183        assert!(analyzer.are_violations_correlated(&double_free, "InvalidFree"));
1184        assert!(!analyzer.are_violations_correlated(&double_free, "PotentialLeak"));
1185    }
1186
1187    #[test]
1188    fn test_analyze_violation() {
1189        let mut analyzer = SecurityViolationAnalyzer::default();
1190        let allocations = vec![AllocationInfo::new(0x1000, 256)];
1191        analyzer.update_allocations(allocations);
1192
1193        let violation = SafetyViolation::DoubleFree {
1194            first_free_stack: CallStackRef::new(0, Some(0)),
1195            second_free_stack: CallStackRef::new(1, Some(0)),
1196            timestamp: 1234567890,
1197        };
1198
1199        let result = analyzer.analyze_violation(&violation, 0x1000);
1200
1201        assert!(result.is_ok());
1202        let violation_id = result.unwrap();
1203        assert!(violation_id.starts_with("SEC-DF-"));
1204
1205        // Check that report was stored
1206        assert_eq!(analyzer.violation_reports.len(), 1);
1207        assert!(analyzer.violation_reports.contains_key(&violation_id));
1208    }
1209
1210    #[test]
1211    fn test_get_all_reports() {
1212        let mut analyzer = SecurityViolationAnalyzer::default();
1213        let violation = SafetyViolation::InvalidFree {
1214            attempted_pointer: 0x1000,
1215            stack: CallStackRef::new(0, Some(0)),
1216            timestamp: 1234567890,
1217        };
1218
1219        let _ = analyzer.analyze_violation(&violation, 0x1000);
1220        let reports = analyzer.get_all_reports();
1221
1222        assert_eq!(reports.len(), 1);
1223    }
1224
1225    #[test]
1226    fn test_get_reports_by_severity() {
1227        let mut analyzer = SecurityViolationAnalyzer::default();
1228
1229        // Add critical violation
1230        let critical_violation = SafetyViolation::DoubleFree {
1231            first_free_stack: CallStackRef::new(0, Some(0)),
1232            second_free_stack: CallStackRef::new(1, Some(0)),
1233            timestamp: 1234567890,
1234        };
1235        let _ = analyzer.analyze_violation(&critical_violation, 0x1000);
1236
1237        // Add medium violation
1238        let medium_violation = SafetyViolation::PotentialLeak {
1239            allocation_stack: CallStackRef::new(0, Some(0)),
1240            allocation_timestamp: 1234567890,
1241            leak_detection_timestamp: 1234567900,
1242        };
1243        let _ = analyzer.analyze_violation(&medium_violation, 0x2000);
1244
1245        let critical_reports = analyzer.get_reports_by_severity(ViolationSeverity::Critical);
1246        let medium_reports = analyzer.get_reports_by_severity(ViolationSeverity::Medium);
1247
1248        assert_eq!(critical_reports.len(), 1);
1249        assert_eq!(medium_reports.len(), 2); // Both critical and medium
1250    }
1251
1252    #[test]
1253    fn test_compute_integrity_hash() {
1254        let analyzer = SecurityViolationAnalyzer::default();
1255        let report = SecurityViolationReport {
1256            violation_id: "test-id".to_string(),
1257            violation_type: "DoubleFree".to_string(),
1258            severity: ViolationSeverity::Critical,
1259            description: "Test violation".to_string(),
1260            technical_details: "Test details".to_string(),
1261            memory_snapshot: MemoryStateSnapshot {
1262                timestamp_ns: 1234567890,
1263                total_allocated_bytes: 1024,
1264                active_allocation_count: 1,
1265                involved_addresses: vec!["0x1000".to_string()],
1266                stack_trace: vec![],
1267                related_allocations: vec![],
1268                memory_pressure: MemoryPressureLevel::Low,
1269            },
1270            impact_assessment: ImpactAssessment {
1271                exploitability_score: 0.9,
1272                data_corruption_risk: true,
1273                information_disclosure_risk: false,
1274                denial_of_service_risk: true,
1275                code_execution_risk: true,
1276                overall_risk_score: 0.9,
1277            },
1278            remediation_suggestions: vec!["Test suggestion".to_string()],
1279            correlated_violations: vec![],
1280            integrity_hash: String::new(),
1281            generated_at_ns: 1234567890,
1282        };
1283
1284        let result = analyzer.compute_integrity_hash(&report);
1285
1286        assert!(result.is_ok());
1287        let hash = result.unwrap();
1288        assert_eq!(hash.len(), 16); // 64-bit hash as hex string
1289    }
1290
1291    #[test]
1292    fn test_verify_report_integrity() {
1293        let config = AnalysisConfig {
1294            generate_integrity_hashes: true,
1295            ..Default::default()
1296        };
1297        let analyzer = SecurityViolationAnalyzer::new(config);
1298
1299        let mut report = SecurityViolationReport {
1300            violation_id: "test-id".to_string(),
1301            violation_type: "DoubleFree".to_string(),
1302            severity: ViolationSeverity::Critical,
1303            description: "Test violation".to_string(),
1304            technical_details: "Test details".to_string(),
1305            memory_snapshot: MemoryStateSnapshot {
1306                timestamp_ns: 1234567890,
1307                total_allocated_bytes: 1024,
1308                active_allocation_count: 1,
1309                involved_addresses: vec!["0x1000".to_string()],
1310                stack_trace: vec![],
1311                related_allocations: vec![],
1312                memory_pressure: MemoryPressureLevel::Low,
1313            },
1314            impact_assessment: ImpactAssessment {
1315                exploitability_score: 0.9,
1316                data_corruption_risk: true,
1317                information_disclosure_risk: false,
1318                denial_of_service_risk: true,
1319                code_execution_risk: true,
1320                overall_risk_score: 0.9,
1321            },
1322            remediation_suggestions: vec!["Test suggestion".to_string()],
1323            correlated_violations: vec![],
1324            integrity_hash: String::new(),
1325            generated_at_ns: 1234567890,
1326        };
1327
1328        // Compute correct hash
1329        let hash = analyzer.compute_integrity_hash(&report).unwrap();
1330        report.integrity_hash = hash;
1331
1332        let result = analyzer.verify_report_integrity(&report);
1333        assert!(result.is_ok());
1334        assert!(result.unwrap());
1335
1336        // Test with wrong hash
1337        report.integrity_hash = "wrong_hash".to_string();
1338        let result = analyzer.verify_report_integrity(&report);
1339        assert!(result.is_ok());
1340        assert!(!result.unwrap());
1341    }
1342
1343    #[test]
1344    fn test_generate_security_summary() {
1345        let mut analyzer = SecurityViolationAnalyzer::default();
1346
1347        // Add some violations
1348        let critical_violation = SafetyViolation::DoubleFree {
1349            first_free_stack: CallStackRef::new(0, Some(0)),
1350            second_free_stack: CallStackRef::new(1, Some(0)),
1351            timestamp: 1234567890,
1352        };
1353        let _ = analyzer.analyze_violation(&critical_violation, 0x1000);
1354
1355        let medium_violation = SafetyViolation::PotentialLeak {
1356            allocation_stack: CallStackRef::new(0, Some(0)),
1357            allocation_timestamp: 1234567890,
1358            leak_detection_timestamp: 1234567900,
1359        };
1360        let _ = analyzer.analyze_violation(&medium_violation, 0x2000);
1361
1362        let summary = analyzer.generate_security_summary();
1363
1364        assert!(summary.is_object());
1365        let security_summary = &summary["security_analysis_summary"];
1366        assert_eq!(security_summary["total_violations"], 2);
1367        assert!(security_summary["severity_breakdown"].is_object());
1368        assert!(security_summary["risk_assessment"].is_object());
1369    }
1370
1371    #[test]
1372    fn test_clear_reports() {
1373        let mut analyzer = SecurityViolationAnalyzer::default();
1374
1375        let violation = SafetyViolation::InvalidFree {
1376            attempted_pointer: 0x1000,
1377            stack: CallStackRef::new(0, Some(0)),
1378            timestamp: 1234567890,
1379        };
1380        let _ = analyzer.analyze_violation(&violation, 0x1000);
1381
1382        assert_eq!(analyzer.violation_reports.len(), 1);
1383
1384        analyzer.clear_reports();
1385
1386        assert_eq!(analyzer.violation_reports.len(), 0);
1387        assert_eq!(analyzer.correlation_matrix.len(), 0);
1388    }
1389
1390    #[test]
1391    fn test_correlation_analysis_disabled() {
1392        let config = AnalysisConfig {
1393            enable_correlation_analysis: false,
1394            ..Default::default()
1395        };
1396        let mut analyzer = SecurityViolationAnalyzer::new(config);
1397
1398        let violation = SafetyViolation::DoubleFree {
1399            first_free_stack: CallStackRef::new(0, Some(0)),
1400            second_free_stack: CallStackRef::new(1, Some(0)),
1401            timestamp: 1234567890,
1402        };
1403
1404        let result = analyzer.analyze_violation(&violation, 0x1000);
1405        assert!(result.is_ok());
1406
1407        let reports = analyzer.get_all_reports();
1408        let report = reports.values().next().unwrap();
1409        assert!(report.correlated_violations.is_empty());
1410    }
1411
1412    #[test]
1413    fn test_integrity_hashes_disabled() {
1414        let config = AnalysisConfig {
1415            generate_integrity_hashes: false,
1416            ..Default::default()
1417        };
1418        let mut analyzer = SecurityViolationAnalyzer::new(config);
1419
1420        let violation = SafetyViolation::InvalidFree {
1421            attempted_pointer: 0x1000,
1422            stack: CallStackRef::new(0, Some(0)),
1423            timestamp: 1234567890,
1424        };
1425
1426        let result = analyzer.analyze_violation(&violation, 0x1000);
1427        assert!(result.is_ok());
1428
1429        let reports = analyzer.get_all_reports();
1430        let report = reports.values().next().unwrap();
1431        assert!(report.integrity_hash.is_empty());
1432    }
1433
1434    #[test]
1435    fn test_max_related_allocations_limit() {
1436        let config = AnalysisConfig {
1437            max_related_allocations: 2,
1438            ..Default::default()
1439        };
1440        let mut analyzer = SecurityViolationAnalyzer::new(config);
1441
1442        // Add many allocations
1443        let allocations: Vec<AllocationInfo> = (0..10)
1444            .map(|i| AllocationInfo::new(0x1000 + i * 0x100, 256))
1445            .collect();
1446        analyzer.update_allocations(allocations);
1447
1448        let related = analyzer.find_related_allocations(0x1100);
1449
1450        // Should be limited to max_related_allocations
1451        assert!(related.len() <= 2);
1452    }
1453
1454    #[test]
1455    fn test_security_violation_report_serialization() {
1456        let report = SecurityViolationReport {
1457            violation_id: "test-id".to_string(),
1458            violation_type: "DoubleFree".to_string(),
1459            severity: ViolationSeverity::Critical,
1460            description: "Test violation".to_string(),
1461            technical_details: "Test details".to_string(),
1462            memory_snapshot: MemoryStateSnapshot {
1463                timestamp_ns: 1234567890,
1464                total_allocated_bytes: 1024,
1465                active_allocation_count: 1,
1466                involved_addresses: vec!["0x1000".to_string()],
1467                stack_trace: vec![],
1468                related_allocations: vec![],
1469                memory_pressure: MemoryPressureLevel::Low,
1470            },
1471            impact_assessment: ImpactAssessment {
1472                exploitability_score: 0.9,
1473                data_corruption_risk: true,
1474                information_disclosure_risk: false,
1475                denial_of_service_risk: true,
1476                code_execution_risk: true,
1477                overall_risk_score: 0.9,
1478            },
1479            remediation_suggestions: vec!["Test suggestion".to_string()],
1480            correlated_violations: vec![],
1481            integrity_hash: "test_hash".to_string(),
1482            generated_at_ns: 1234567890,
1483        };
1484
1485        let serialized = serde_json::to_string(&report);
1486        assert!(serialized.is_ok());
1487
1488        let deserialized: Result<SecurityViolationReport, _> =
1489            serde_json::from_str(&serialized.unwrap());
1490        assert!(deserialized.is_ok());
1491    }
1492}