//! Integration tests for Ghost detection engine. #[cfg(test)] mod tests { use ghost_core::{ config::DetectionConfig, DetectionEngine, MemoryProtection, MemoryRegion, ProcessInfo, ThreatLevel, }; fn create_test_process() -> ProcessInfo { ProcessInfo { pid: 1234, ppid: 4, name: "test.exe".to_string(), path: Some("C:\\Windows\\System32\\test.exe".to_string()), thread_count: 1, } } fn create_rwx_region() -> MemoryRegion { MemoryRegion { base_address: 0x10000000, size: 0x1000, protection: MemoryProtection::ReadWriteExecute, region_type: "PRIVATE".to_string(), } } #[test] fn test_clean_process_detection() { let mut engine = DetectionEngine::new().expect("Failed to create engine"); let process = create_test_process(); let regions = vec![MemoryRegion { base_address: 0x400000, size: 0x10000, protection: MemoryProtection::ReadExecute, region_type: "IMAGE".to_string(), }]; let result = engine.analyze_process(&process, ®ions, None); assert_eq!(result.threat_level, ThreatLevel::Clean); assert!(result.indicators.is_empty()); } #[test] fn test_rwx_region_detection() { let mut engine = DetectionEngine::new().expect("Failed to create engine"); let process = create_test_process(); let regions = vec![create_rwx_region()]; let result = engine.analyze_process(&process, ®ions, None); assert_ne!(result.threat_level, ThreatLevel::Clean); assert!(!result.indicators.is_empty()); assert!(result.indicators[0].contains("RWX")); } #[test] fn test_multiple_small_executable_regions() { let mut engine = DetectionEngine::new().expect("Failed to create engine"); let process = create_test_process(); let regions = vec![ MemoryRegion { base_address: 0x10000000, size: 0x800, // Small size protection: MemoryProtection::ReadExecute, region_type: "PRIVATE".to_string(), }, MemoryRegion { base_address: 0x20000000, size: 0x600, // Small size protection: MemoryProtection::ReadExecute, region_type: "PRIVATE".to_string(), }, MemoryRegion { base_address: 0x30000000, size: 0x400, // Small size protection: MemoryProtection::ReadExecute, region_type: "PRIVATE".to_string(), }, ]; let result = engine.analyze_process(&process, ®ions, None); assert!(result.confidence > 0.0); assert!(result .indicators .iter() .any(|i| i.contains("small executable"))); } #[test] fn test_baseline_tracking() { let mut engine = DetectionEngine::new().expect("Failed to create engine"); let mut process = create_test_process(); let regions = vec![]; // First scan establishes baseline let result1 = engine.analyze_process(&process, ®ions, None); assert_eq!(result1.threat_level, ThreatLevel::Clean); // Second scan with increased thread count process.thread_count = 5; let result2 = engine.analyze_process(&process, ®ions, None); assert!(result2 .indicators .iter() .any(|i| i.contains("new threads"))); } #[test] fn test_multiple_rwx_regions_high_severity() { let mut engine = DetectionEngine::new().expect("Failed to create engine"); let process = create_test_process(); let regions = vec![ MemoryRegion { base_address: 0x10000000, size: 0x1000, protection: MemoryProtection::ReadWriteExecute, region_type: "PRIVATE".to_string(), }, MemoryRegion { base_address: 0x20000000, size: 0x2000, protection: MemoryProtection::ReadWriteExecute, region_type: "PRIVATE".to_string(), }, MemoryRegion { base_address: 0x30000000, size: 0x3000, protection: MemoryProtection::ReadWriteExecute, region_type: "PRIVATE".to_string(), }, ]; let result = engine.analyze_process(&process, ®ions, None); // Multiple RWX regions should be highly suspicious assert_eq!(result.threat_level, ThreatLevel::Malicious); assert!(result.confidence >= 0.5); } #[test] fn test_memory_protection_display() { assert_eq!(format!("{}", MemoryProtection::NoAccess), "---"); assert_eq!(format!("{}", MemoryProtection::ReadOnly), "R--"); assert_eq!(format!("{}", MemoryProtection::ReadWrite), "RW-"); assert_eq!(format!("{}", MemoryProtection::ReadExecute), "R-X"); assert_eq!(format!("{}", MemoryProtection::ReadWriteExecute), "RWX"); assert_eq!(format!("{}", MemoryProtection::Execute), "--X"); } #[test] fn test_process_info_display() { let process = create_test_process(); let display = format!("{}", process); assert!(display.contains("1234")); assert!(display.contains("test.exe")); } #[test] fn test_memory_region_display() { let region = create_rwx_region(); let display = format!("{}", region); assert!(display.contains("RWX")); assert!(display.contains("PRIVATE")); } #[test] fn test_threat_level_ordering() { assert!(ThreatLevel::Clean < ThreatLevel::Suspicious); assert!(ThreatLevel::Suspicious < ThreatLevel::Malicious); } #[test] fn test_detection_config_validation() { let config = DetectionConfig::default(); assert!(config.validate().is_ok()); let mut invalid_config = DetectionConfig::default(); invalid_config.confidence_threshold = 1.5; // Invalid assert!(invalid_config.validate().is_err()); invalid_config.confidence_threshold = -0.1; // Invalid assert!(invalid_config.validate().is_err()); } #[test] fn test_detection_config_presets() { let perf_config = DetectionConfig::performance_mode(); let thorough_config = DetectionConfig::thorough_mode(); // Performance mode should have lower thresholds for faster scanning assert!(perf_config.confidence_threshold <= thorough_config.confidence_threshold); } #[test] fn test_process_is_system_process() { let mut process = create_test_process(); assert!(!process.is_system_process()); process.pid = 0; assert!(process.is_system_process()); process.pid = 4; assert!(process.is_system_process()); process.pid = 100; process.name = "System".to_string(); assert!(process.is_system_process()); } #[test] fn test_engine_with_custom_config() { let mut config = DetectionConfig::default(); config.rwx_detection = false; let mut engine = DetectionEngine::with_config(config).expect("Failed to create engine"); let process = create_test_process(); let regions = vec![create_rwx_region()]; // With RWX detection disabled, should not flag the region let result = engine.analyze_process(&process, ®ions, None); // Might still detect based on other heuristics, but confidence should be lower assert!(result.confidence < 0.5); } #[test] fn test_large_memory_region() { let mut engine = DetectionEngine::new().expect("Failed to create engine"); let process = create_test_process(); let regions = vec![MemoryRegion { base_address: 0x10000000, size: 100 * 1024 * 1024, // 100MB region protection: MemoryProtection::ReadWriteExecute, region_type: "PRIVATE".to_string(), }]; let result = engine.analyze_process(&process, ®ions, None); assert_ne!(result.threat_level, ThreatLevel::Clean); } #[test] fn test_image_vs_private_region() { let mut engine = DetectionEngine::new().expect("Failed to create engine"); let process = create_test_process(); // IMAGE region with RX is normal let image_regions = vec![MemoryRegion { base_address: 0x400000, size: 0x100000, protection: MemoryProtection::ReadExecute, region_type: "IMAGE".to_string(), }]; let result = engine.analyze_process(&process, &image_regions, None); assert_eq!(result.threat_level, ThreatLevel::Clean); // PRIVATE region with RX is suspicious let private_regions = vec![MemoryRegion { base_address: 0x10000000, size: 0x1000, protection: MemoryProtection::ReadExecute, region_type: "PRIVATE".to_string(), }]; let result2 = engine.analyze_process(&process, &private_regions, None); // Private executable regions are suspicious but not as severe as RWX assert!(result2.confidence > 0.0 || result2.indicators.len() > 0); } } #[cfg(test)] mod mitre_tests { use ghost_core::mitre::{MitreMapping, TechniqueId}; #[test] fn test_technique_id_display() { let id = TechniqueId::new("T1055", Some("001")); assert_eq!(format!("{}", id), "T1055.001"); let id_no_sub = TechniqueId::new("T1055", None); assert_eq!(format!("{}", id_no_sub), "T1055"); } #[test] fn test_mitre_mapping_creation() { let mapping = MitreMapping::default(); assert!(mapping.techniques.is_empty()); } #[test] fn test_technique_lookup() { let mapping = MitreMapping::default(); // Default mapping should have no techniques initially assert!(mapping.get_technique("T1055").is_none()); } } #[cfg(test)] mod threat_intel_tests { use ghost_core::ThreatLevel; #[test] fn test_threat_level_description() { assert_eq!(ThreatLevel::Clean.description(), "No threats detected"); assert_eq!( ThreatLevel::Suspicious.description(), "Potential security concern" ); assert_eq!( ThreatLevel::Malicious.description(), "High confidence malicious activity" ); } #[test] fn test_threat_level_serialization() { let level = ThreatLevel::Suspicious; let serialized = serde_json::to_string(&level).expect("Failed to serialize"); assert!(serialized.contains("Suspicious")); let deserialized: ThreatLevel = serde_json::from_str(&serialized).expect("Failed to deserialize"); assert_eq!(deserialized, level); } } #[cfg(test)] mod config_tests { use ghost_core::config::DetectionConfig; #[test] fn test_default_config() { let config = DetectionConfig::default(); assert!(config.rwx_detection); assert!(config.shellcode_detection); assert!(config.hollowing_detection); assert!(config.thread_detection); assert!(config.hook_detection); } #[test] fn test_config_serialization() { let config = DetectionConfig::default(); let json = serde_json::to_string(&config).expect("Failed to serialize"); let deserialized: DetectionConfig = serde_json::from_str(&json).expect("Failed to deserialize"); assert_eq!(config.rwx_detection, deserialized.rwx_detection); } #[test] fn test_config_toml_format() { let config = DetectionConfig::default(); let toml_str = toml::to_string(&config).expect("Failed to serialize to TOML"); assert!(toml_str.contains("rwx_detection")); assert!(toml_str.contains("confidence_threshold")); } }