1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum ViolationSeverity {
20 Critical,
22 High,
24 Medium,
26 Low,
28 Info,
30}
31
32impl ViolationSeverity {
33 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#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct MemoryStateSnapshot {
48 pub timestamp_ns: u64,
50 pub total_allocated_bytes: usize,
52 pub active_allocation_count: usize,
54 pub involved_addresses: Vec<String>, pub stack_trace: Vec<StackFrame>,
58 pub related_allocations: Vec<RelatedAllocation>,
60 pub memory_pressure: MemoryPressureLevel,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct StackFrame {
67 pub function_name: String,
69 pub file_path: Option<String>,
71 pub line_number: Option<u32>,
73 pub frame_address: String, pub is_unsafe: bool,
77 pub is_ffi: bool,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct RelatedAllocation {
84 pub address: String, pub size: usize,
88 pub type_name: Option<String>,
90 pub variable_name: Option<String>,
92 pub allocated_at_ns: u64,
94 pub is_active: bool,
96 pub relationship: AllocationRelationship,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub enum AllocationRelationship {
103 SameRegion,
105 Adjacent,
107 SameType,
109 SameScope,
111 DoubleFreeCandidate,
113 LeakRelated,
115 UseAfterFreeRelated,
117 None,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub enum MemoryPressureLevel {
124 Low,
126 Medium,
128 High,
130 Critical,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct SecurityViolationReport {
137 pub violation_id: String,
139 pub violation_type: String,
141 pub severity: ViolationSeverity,
143 pub description: String,
145 pub technical_details: String,
147 pub memory_snapshot: MemoryStateSnapshot,
149 pub impact_assessment: ImpactAssessment,
151 pub remediation_suggestions: Vec<String>,
153 pub correlated_violations: Vec<String>, pub integrity_hash: String,
157 pub generated_at_ns: u64,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct ImpactAssessment {
164 pub exploitability_score: f64,
166 pub data_corruption_risk: bool,
168 pub information_disclosure_risk: bool,
170 pub denial_of_service_risk: bool,
172 pub code_execution_risk: bool,
174 pub overall_risk_score: f64,
176}
177
178#[derive(Debug)]
180pub struct SecurityViolationAnalyzer {
181 violation_reports: HashMap<String, SecurityViolationReport>,
183 correlation_matrix: HashMap<String, HashSet<String>>,
185 active_allocations: Vec<AllocationInfo>,
187 config: AnalysisConfig,
189}
190
191#[derive(Debug, Clone)]
193pub struct AnalysisConfig {
194 pub max_related_allocations: usize,
196 pub max_stack_depth: usize,
198 pub enable_correlation_analysis: bool,
200 pub include_low_severity: bool,
202 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 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 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 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 let severity = self.assess_severity(violation);
261
262 let memory_snapshot = self.create_memory_snapshot(violation_address)?;
264
265 let (description, technical_details) = self.generate_violation_details(violation);
267
268 let impact_assessment = self.assess_impact(violation, &memory_snapshot);
270
271 let remediation_suggestions =
273 self.generate_remediation_suggestions(violation, &impact_assessment);
274
275 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 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(), generated_at_ns: SystemTime::now()
295 .duration_since(UNIX_EPOCH)
296 .unwrap_or_default()
297 .as_nanos() as u64,
298 };
299
300 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 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 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 fn assess_severity(&self, violation: &SafetyViolation) -> ViolationSeverity {
342 match violation {
343 SafetyViolation::DoubleFree { .. } => ViolationSeverity::Critical,
344 SafetyViolation::InvalidFree { .. } => ViolationSeverity::High,
345 SafetyViolation::PotentialLeak { .. } => {
346 ViolationSeverity::Medium
348 }
349 SafetyViolation::CrossBoundaryRisk { .. } => ViolationSeverity::Medium,
350 }
351 }
352
353 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 let related_allocations = self.find_related_allocations(violation_address);
369
370 let stack_trace = self.generate_stack_trace();
372
373 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 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 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 if violation_addr >= alloc_start && violation_addr < alloc_end {
425 return Some(AllocationRelationship::SameRegion);
426 }
427
428 if (violation_addr as isize - alloc_end as isize).abs() < 64 {
430 return Some(AllocationRelationship::Adjacent);
431 }
432
433 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 fn generate_stack_trace(&self) -> Vec<StackFrame> {
445 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 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 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 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 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 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 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 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 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 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 fn compute_integrity_hash(&self, report: &SecurityViolationReport) -> Result<String, String> {
632 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 pub fn get_all_reports(&self) -> &HashMap<String, SecurityViolationReport> {
650 &self.violation_reports
651 }
652
653 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 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); }
673
674 let computed_hash = self.compute_integrity_hash(report)?;
675 Ok(computed_hash == report.integrity_hash)
676 }
677
678 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 }
726 }
727 })
728 }
729
730 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), AllocationInfo::new(0x2000, 512), ];
1013 analyzer.update_allocations(allocations);
1014
1015 let related = analyzer.find_related_allocations(0x1100); 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 let same_region = analyzer.determine_relationship(0x1080, &alloc);
1029 assert!(matches!(
1030 same_region,
1031 Some(AllocationRelationship::SameRegion)
1032 ));
1033
1034 let adjacent = analyzer.determine_relationship(0x1100 + 32, &alloc);
1036 assert!(matches!(adjacent, Some(AllocationRelationship::Adjacent)));
1037
1038 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); matches!(low, MemoryPressureLevel::Low);
1060
1061 let medium = analyzer.assess_memory_pressure(600 * 1024 * 1024); matches!(medium, MemoryPressureLevel::Medium);
1063
1064 let high = analyzer.assess_memory_pressure(1200 * 1024 * 1024); matches!(high, MemoryPressureLevel::High);
1066
1067 let critical = analyzer.assess_memory_pressure(3000 * 1024 * 1024); 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 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 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 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); }
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); }
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 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 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 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 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 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}