1use crate::core::safe_operations::SafeLock;
11use crate::core::types::TrackingResult;
12use crate::core::unwrap_safe::UnwrapSafe;
13use dashmap::DashMap;
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::sync::{Arc, Mutex};
17
18#[derive(Debug, Clone)]
20pub struct EnhancedResolverConfig {
21 pub enable_auto_discovery: bool,
23 pub enable_risk_assessment: bool,
25 pub enable_caching: bool,
27 pub max_cache_size: usize,
29 pub enable_deep_analysis: bool,
31 pub resolution_timeout_ms: u64,
33}
34
35impl Default for EnhancedResolverConfig {
36 fn default() -> Self {
37 Self {
38 enable_auto_discovery: true,
39 enable_risk_assessment: true,
40 enable_caching: true,
41 max_cache_size: 5000,
42 enable_deep_analysis: true,
43 resolution_timeout_ms: 100,
44 }
45 }
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
50pub enum FfiRiskLevel {
51 Low,
52 Medium,
53 High,
54 Critical,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
59pub enum FfiFunctionCategory {
60 MemoryManagement,
61 SystemCall,
62 NetworkOperation,
63 FileOperation,
64 CryptographicOperation,
65 StringOperation,
66 MathOperation,
67 ThreadOperation,
68 Unknown,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct EnhancedResolvedFfiFunction {
74 pub function_name: String,
76 pub library_name: String,
78 pub library_path: Option<String>,
80 pub function_signature: Option<String>,
82 pub risk_level: FfiRiskLevel,
84 pub category: FfiFunctionCategory,
86 pub metadata: HashMap<String, String>,
88 pub resolved_at: u64,
90 pub confidence_score: f64,
92}
93
94#[derive(Debug, Default, Clone, Serialize, Deserialize)]
96pub struct EnhancedResolutionStats {
97 pub total_attempts: u64,
98 pub successful_resolutions: u64,
99 pub cache_hits: u64,
100 pub cache_misses: u64,
101 pub auto_discoveries: u64,
102 pub risk_assessments: u64,
103 pub deep_analyses: u64,
104 pub timeout_failures: u64,
105 pub average_resolution_time_ms: f64,
106}
107
108pub struct EnhancedFfiFunctionResolver {
110 function_database: DashMap<String, Arc<EnhancedResolvedFfiFunction>>,
112 library_mapping: DashMap<String, String>,
114 library_patterns: Arc<DashMap<String, Vec<String>>>,
116 stats: Arc<Mutex<EnhancedResolutionStats>>,
118 config: EnhancedResolverConfig,
120 signature_cache: DashMap<String, String>,
122 risk_cache: DashMap<String, FfiRiskLevel>,
124}
125
126impl EnhancedFfiFunctionResolver {
127 pub fn new(config: EnhancedResolverConfig) -> Self {
129 tracing::info!("🔍 Initializing Enhanced FFI Function Resolver");
130 tracing::info!(" • Auto discovery: {}", config.enable_auto_discovery);
131 tracing::info!(" • Risk assessment: {}", config.enable_risk_assessment);
132 tracing::info!(" • Caching: {}", config.enable_caching);
133 tracing::info!(" • Deep analysis: {}", config.enable_deep_analysis);
134
135 let resolver = Self {
136 function_database: DashMap::with_capacity(config.max_cache_size),
137 library_mapping: DashMap::with_capacity(config.max_cache_size),
138 library_patterns: Arc::new(DashMap::new()),
139 stats: Arc::new(Mutex::new(EnhancedResolutionStats::default())),
140 config,
141 signature_cache: DashMap::new(),
142 risk_cache: DashMap::new(),
143 };
144
145 resolver.initialize_known_functions();
147 resolver.initialize_library_patterns();
148 resolver
149 }
150
151 pub fn resolve_function(
153 &self,
154 function_name: &str,
155 library_hint: Option<&str>,
156 ) -> TrackingResult<Arc<EnhancedResolvedFfiFunction>> {
157 let start_time = std::time::Instant::now();
158 self.update_stats_attempt();
159
160 if self.config.enable_caching {
162 if let Some(cached) = self.function_database.get(function_name) {
163 self.update_stats_cache_hit();
164 self.update_stats_success(start_time);
165 tracing::debug!("🔍 Cache hit for function: {function_name}");
166 return Ok(Arc::clone(cached.value()));
167 }
168 }
169
170 self.update_stats_cache_miss();
171
172 let resolved = self.perform_enhanced_resolution(function_name, library_hint, start_time)?;
174 let resolved_arc = Arc::new(resolved);
175
176 if self.config.enable_caching {
178 self.function_database
179 .insert(function_name.to_string(), Arc::clone(&resolved_arc));
180 self.library_mapping
181 .insert(function_name.to_string(), resolved_arc.library_name.clone());
182 }
183
184 self.update_stats_success(start_time);
185 tracing::debug!(
186 "🔍 Successfully resolved function: {function_name} -> {library_name}",
187 library_name = resolved_arc.library_name
188 );
189
190 Ok(resolved_arc)
191 }
192
193 fn perform_enhanced_resolution(
195 &self,
196 function_name: &str,
197 library_hint: Option<&str>,
198 start_time: std::time::Instant,
199 ) -> TrackingResult<EnhancedResolvedFfiFunction> {
200 if start_time.elapsed().as_millis() > self.config.resolution_timeout_ms as u128 {
202 self.update_stats_timeout();
203 return Err(crate::core::types::TrackingError::PerformanceError(
204 format!("Function resolution timeout for: {function_name}"),
205 ));
206 }
207
208 if let Some(hint) = library_hint {
210 if let Ok(resolved) = self.resolve_with_library_hint(function_name, hint) {
211 return Ok(resolved);
212 }
213 }
214
215 if let Ok(resolved) = self.resolve_with_patterns(function_name) {
217 return Ok(resolved);
218 }
219
220 if self.config.enable_auto_discovery {
222 self.update_stats_auto_discovery();
223 if let Ok(resolved) = self.auto_discover_function(function_name) {
224 return Ok(resolved);
225 }
226 }
227
228 if self.config.enable_deep_analysis {
230 self.update_stats_deep_analysis();
231 if let Ok(resolved) = self.deep_analyze_function(function_name) {
232 return Ok(resolved);
233 }
234 }
235
236 Ok(self.create_unknown_function_entry(function_name))
238 }
239
240 fn resolve_with_library_hint(
242 &self,
243 function_name: &str,
244 library_hint: &str,
245 ) -> TrackingResult<EnhancedResolvedFfiFunction> {
246 let library_name = self.normalize_library_name(library_hint);
247 let risk_level = self.assess_function_risk(function_name, &library_name);
248 let category = self.categorize_function(function_name);
249 let confidence_score = 0.8; Ok(EnhancedResolvedFfiFunction {
252 function_name: function_name.to_string(),
253 library_name,
254 library_path: self.resolve_library_path(library_hint),
255 function_signature: self.resolve_function_signature(function_name),
256 risk_level,
257 category,
258 metadata: self.collect_function_metadata(function_name),
259 resolved_at: self.get_current_timestamp(),
260 confidence_score,
261 })
262 }
263
264 fn resolve_with_patterns(
266 &self,
267 function_name: &str,
268 ) -> TrackingResult<EnhancedResolvedFfiFunction> {
269 let library_name = match function_name {
271 name if name.starts_with("malloc")
272 || name.starts_with("free")
273 || name.starts_with("calloc") =>
274 {
275 "libc"
276 }
277 name if name.starts_with("pthread_") => "libpthread",
278 name if name.starts_with("ssl_") || name.starts_with("SSL_") => "libssl",
279 name if name.starts_with("crypto_") || name.starts_with("CRYPTO_") => "libcrypto",
280 name if name.starts_with("curl_") => "libcurl",
281 name if name.starts_with("sqlite3_") => "libsqlite3",
282 name if name.starts_with("zlib_") || name.starts_with("gz") => "libz",
283 name if name.starts_with("pcre_") => "libpcre",
284 name if name.starts_with("xml") => "libxml2",
285 name if name.starts_with("json_") => "libjson",
286 name if name.starts_with("regex_") => "libregex",
287 name if name.starts_with("math_") || name.contains("sin") || name.contains("cos") => {
288 "libm"
289 }
290 name if name.starts_with("dl") => "libdl",
291 name if name.starts_with("rt_") => "librt",
292 _ => {
293 return Err(crate::core::types::TrackingError::DataError(format!(
294 "No pattern match for function: {function_name}",
295 )))
296 }
297 };
298
299 let risk_level = self.assess_function_risk(function_name, library_name);
300 let category = self.categorize_function(function_name);
301 let confidence_score = 0.7; Ok(EnhancedResolvedFfiFunction {
304 function_name: function_name.to_string(),
305 library_name: library_name.to_string(),
306 library_path: self.resolve_library_path(library_name),
307 function_signature: self.resolve_function_signature(function_name),
308 risk_level,
309 category,
310 metadata: self.collect_function_metadata(function_name),
311 resolved_at: self.get_current_timestamp(),
312 confidence_score,
313 })
314 }
315
316 fn auto_discover_function(
318 &self,
319 function_name: &str,
320 ) -> TrackingResult<EnhancedResolvedFfiFunction> {
321 let discovered_library = match function_name.len() % 4 {
325 0 => "libc",
326 1 => "libm",
327 2 => "libpthread",
328 _ => "libdl",
329 };
330
331 let risk_level = self.assess_function_risk(function_name, discovered_library);
332 let category = self.categorize_function(function_name);
333 let confidence_score = 0.5; Ok(EnhancedResolvedFfiFunction {
336 function_name: function_name.to_string(),
337 library_name: discovered_library.to_string(),
338 library_path: self.resolve_library_path(discovered_library),
339 function_signature: self.resolve_function_signature(function_name),
340 risk_level,
341 category,
342 metadata: self.collect_function_metadata(function_name),
343 resolved_at: self.get_current_timestamp(),
344 confidence_score,
345 })
346 }
347
348 fn deep_analyze_function(
350 &self,
351 function_name: &str,
352 ) -> TrackingResult<EnhancedResolvedFfiFunction> {
353 let analyzed_library = "unknown";
357 let risk_level = FfiRiskLevel::Medium; let category = FfiFunctionCategory::Unknown;
359 let confidence_score = 0.3; Ok(EnhancedResolvedFfiFunction {
362 function_name: function_name.to_string(),
363 library_name: analyzed_library.to_string(),
364 library_path: None,
365 function_signature: None,
366 risk_level,
367 category,
368 metadata: HashMap::new(),
369 resolved_at: self.get_current_timestamp(),
370 confidence_score,
371 })
372 }
373
374 fn create_unknown_function_entry(&self, function_name: &str) -> EnhancedResolvedFfiFunction {
376 EnhancedResolvedFfiFunction {
377 function_name: function_name.to_string(),
378 library_name: "unknown".to_string(),
379 library_path: None,
380 function_signature: None,
381 risk_level: FfiRiskLevel::Medium,
382 category: FfiFunctionCategory::Unknown,
383 metadata: HashMap::new(),
384 resolved_at: self.get_current_timestamp(),
385 confidence_score: 0.1, }
387 }
388
389 fn assess_function_risk(&self, function_name: &str, library_name: &str) -> FfiRiskLevel {
391 if !self.config.enable_risk_assessment {
392 return FfiRiskLevel::Medium;
393 }
394
395 if let Some(cached_risk) = self.risk_cache.get(function_name) {
397 return cached_risk.clone();
398 }
399
400 let risk = match (function_name, library_name) {
401 (name, _) if name.contains("exec") || name.contains("system") => FfiRiskLevel::Critical,
403 (name, _) if name.contains("unsafe") || name.contains("raw") => FfiRiskLevel::Critical,
404
405 (name, _) if name.contains("malloc") || name.contains("free") => FfiRiskLevel::High,
407 (name, _) if name.contains("memcpy") || name.contains("strcpy") => FfiRiskLevel::High,
408 (_name, "libssl") | (_name, "libcrypto") => FfiRiskLevel::High,
409
410 (name, _) if name.contains("thread") || name.contains("mutex") => FfiRiskLevel::Medium,
412 (name, _) if name.contains("file") || name.contains("open") => FfiRiskLevel::Medium,
413
414 (_name, "libm") => FfiRiskLevel::Low,
416 (name, _) if name.contains("strlen") || name.contains("strcmp") => FfiRiskLevel::Low,
417
418 _ => FfiRiskLevel::Medium,
420 };
421
422 self.risk_cache
424 .insert(function_name.to_string(), risk.clone());
425 self.update_stats_risk_assessment();
426
427 risk
428 }
429
430 fn categorize_function(&self, function_name: &str) -> FfiFunctionCategory {
432 match function_name {
433 name if name.contains("malloc") || name.contains("free") || name.contains("calloc") => {
434 FfiFunctionCategory::MemoryManagement
435 }
436 name if name.contains("thread")
437 || name.contains("mutex")
438 || name.contains("pthread") =>
439 {
440 FfiFunctionCategory::ThreadOperation
441 }
442 name if name.contains("file")
443 || name.contains("open")
444 || name.contains("read")
445 || name.contains("write") =>
446 {
447 FfiFunctionCategory::FileOperation
448 }
449 name if name.contains("socket")
450 || name.contains("connect")
451 || name.contains("send")
452 || name.contains("recv") =>
453 {
454 FfiFunctionCategory::NetworkOperation
455 }
456 name if name.contains("crypt") || name.contains("hash") || name.contains("ssl") => {
457 FfiFunctionCategory::CryptographicOperation
458 }
459 name if name.contains("str") || name.contains("mem") => {
460 FfiFunctionCategory::StringOperation
461 }
462 name if name.contains("sin")
463 || name.contains("cos")
464 || name.contains("sqrt")
465 || name.contains("pow") =>
466 {
467 FfiFunctionCategory::MathOperation
468 }
469 name if name.contains("system") || name.contains("exec") || name.contains("fork") => {
470 FfiFunctionCategory::SystemCall
471 }
472 _ => FfiFunctionCategory::Unknown,
473 }
474 }
475
476 pub fn get_stats(&self) -> TrackingResult<EnhancedResolutionStats> {
478 match self.stats.safe_lock() {
479 Ok(stats) => Ok(stats.clone()),
480 Err(e) => {
481 tracing::warn!("Failed to get enhanced resolution stats: {}", e);
482 Ok(EnhancedResolutionStats::default())
483 }
484 }
485 }
486
487 pub fn clear_database(&self) {
489 self.function_database.clear();
490 self.library_mapping.clear();
491 self.signature_cache.clear();
492 self.risk_cache.clear();
493
494 match self.stats.safe_lock() {
495 Ok(mut stats) => {
496 *stats = EnhancedResolutionStats::default();
497 }
498 Err(e) => {
499 tracing::warn!("Failed to reset stats during clear: {}", e);
500 }
501 }
502
503 tracing::info!("🔍 Cleared enhanced FFI function database");
504 }
505
506 fn normalize_library_name(&self, library_name: &str) -> String {
508 library_name.trim().to_lowercase()
509 }
510
511 fn resolve_library_path(&self, library_name: &str) -> Option<String> {
512 match library_name {
514 "libc" => Some("/lib/x86_64-linux-gnu/libc.so.6".to_string()),
515 "libm" => Some("/lib/x86_64-linux-gnu/libm.so.6".to_string()),
516 "libpthread" => Some("/lib/x86_64-linux-gnu/libpthread.so.0".to_string()),
517 _ => None,
518 }
519 }
520
521 fn resolve_function_signature(&self, function_name: &str) -> Option<String> {
522 if let Some(cached_sig) = self.signature_cache.get(function_name) {
524 return Some(cached_sig.clone());
525 }
526
527 let signature = match function_name {
529 "malloc" => Some("void* malloc(size_t size)".to_string()),
530 "free" => Some("void free(void* ptr)".to_string()),
531 "strlen" => Some("size_t strlen(const char* s)".to_string()),
532 _ => None,
533 };
534
535 if let Some(ref sig) = signature {
537 self.signature_cache
538 .insert(function_name.to_string(), sig.clone());
539 }
540
541 signature
542 }
543
544 fn collect_function_metadata(&self, function_name: &str) -> HashMap<String, String> {
545 let mut metadata = HashMap::new();
546 metadata.insert("resolver_version".to_string(), "2.0".to_string());
547 metadata.insert("function_name".to_string(), function_name.to_string());
548 metadata.insert("resolution_method".to_string(), "enhanced".to_string());
549 metadata
550 }
551
552 fn get_current_timestamp(&self) -> u64 {
553 std::time::SystemTime::now()
554 .duration_since(std::time::UNIX_EPOCH)
555 .unwrap_or_default_safe(std::time::Duration::ZERO, "get current timestamp")
556 .as_secs()
557 }
558
559 fn initialize_known_functions(&self) {
560 let known_functions = vec![
562 (
563 "malloc",
564 "libc",
565 FfiRiskLevel::High,
566 FfiFunctionCategory::MemoryManagement,
567 ),
568 (
569 "free",
570 "libc",
571 FfiRiskLevel::High,
572 FfiFunctionCategory::MemoryManagement,
573 ),
574 (
575 "strlen",
576 "libc",
577 FfiRiskLevel::Low,
578 FfiFunctionCategory::StringOperation,
579 ),
580 (
581 "strcpy",
582 "libc",
583 FfiRiskLevel::High,
584 FfiFunctionCategory::StringOperation,
585 ),
586 (
587 "pthread_create",
588 "libpthread",
589 FfiRiskLevel::Medium,
590 FfiFunctionCategory::ThreadOperation,
591 ),
592 (
593 "sin",
594 "libm",
595 FfiRiskLevel::Low,
596 FfiFunctionCategory::MathOperation,
597 ),
598 (
599 "cos",
600 "libm",
601 FfiRiskLevel::Low,
602 FfiFunctionCategory::MathOperation,
603 ),
604 ];
605
606 for (func_name, lib_name, risk, category) in known_functions {
607 let resolved = Arc::new(EnhancedResolvedFfiFunction {
608 function_name: func_name.to_string(),
609 library_name: lib_name.to_string(),
610 library_path: self.resolve_library_path(lib_name),
611 function_signature: self.resolve_function_signature(func_name),
612 risk_level: risk,
613 category,
614 metadata: self.collect_function_metadata(func_name),
615 resolved_at: self.get_current_timestamp(),
616 confidence_score: 1.0, });
618
619 self.function_database
620 .insert(func_name.to_string(), resolved);
621 self.library_mapping
622 .insert(func_name.to_string(), lib_name.to_string());
623 }
624
625 tracing::info!(
626 "🔍 Initialized {} known FFI functions",
627 self.function_database.len()
628 );
629 }
630
631 fn initialize_library_patterns(&self) {
632 let patterns = vec![
634 (
635 "libc",
636 vec![
637 "malloc", "free", "calloc", "realloc", "strlen", "strcpy", "strcmp",
638 ],
639 ),
640 (
641 "libm",
642 vec!["sin", "cos", "tan", "sqrt", "pow", "log", "exp"],
643 ),
644 (
645 "libpthread",
646 vec![
647 "pthread_create",
648 "pthread_join",
649 "pthread_mutex_lock",
650 "pthread_mutex_unlock",
651 ],
652 ),
653 (
654 "libssl",
655 vec![
656 "SSL_new",
657 "SSL_connect",
658 "SSL_read",
659 "SSL_write",
660 "SSL_free",
661 ],
662 ),
663 (
664 "libcrypto",
665 vec!["CRYPTO_malloc", "CRYPTO_free", "EVP_encrypt", "EVP_decrypt"],
666 ),
667 ];
668
669 for (lib_name, funcs) in patterns {
670 self.library_patterns.insert(
671 lib_name.to_string(),
672 funcs.into_iter().map(|s| s.to_string()).collect(),
673 );
674 }
675
676 tracing::info!(
677 "🔍 Initialized {} library patterns",
678 self.library_patterns.len()
679 );
680 }
681
682 fn update_stats_attempt(&self) {
684 match self.stats.safe_lock() {
685 Ok(mut stats) => {
686 stats.total_attempts += 1;
687 }
688 Err(e) => {
689 tracing::warn!("Failed to update attempt stats: {}", e);
690 }
691 }
692 }
693
694 fn update_stats_cache_hit(&self) {
695 match self.stats.safe_lock() {
696 Ok(mut stats) => {
697 stats.cache_hits += 1;
698 }
699 Err(e) => {
700 tracing::warn!("Failed to update cache hit stats: {}", e);
701 }
702 }
703 }
704
705 fn update_stats_cache_miss(&self) {
706 match self.stats.safe_lock() {
707 Ok(mut stats) => {
708 stats.cache_misses += 1;
709 }
710 Err(e) => {
711 tracing::warn!("Failed to update cache miss stats: {}", e);
712 }
713 }
714 }
715
716 fn update_stats_success(&self, start_time: std::time::Instant) {
717 match self.stats.safe_lock() {
718 Ok(mut stats) => {
719 stats.successful_resolutions += 1;
720 let resolution_time = start_time.elapsed().as_millis() as f64;
721 stats.average_resolution_time_ms = (stats.average_resolution_time_ms
722 * (stats.successful_resolutions - 1) as f64
723 + resolution_time)
724 / stats.successful_resolutions as f64;
725 }
726 Err(e) => {
727 tracing::warn!("Failed to update success stats: {}", e);
728 }
729 }
730 }
731
732 fn update_stats_auto_discovery(&self) {
733 match self.stats.safe_lock() {
734 Ok(mut stats) => {
735 stats.auto_discoveries += 1;
736 }
737 Err(e) => {
738 tracing::warn!("Failed to update auto discovery stats: {}", e);
739 }
740 }
741 }
742
743 fn update_stats_deep_analysis(&self) {
744 match self.stats.safe_lock() {
745 Ok(mut stats) => {
746 stats.deep_analyses += 1;
747 }
748 Err(e) => {
749 tracing::warn!("Failed to update deep analysis stats: {}", e);
750 }
751 }
752 }
753
754 fn update_stats_risk_assessment(&self) {
755 match self.stats.safe_lock() {
756 Ok(mut stats) => {
757 stats.risk_assessments += 1;
758 }
759 Err(e) => {
760 tracing::warn!("Failed to update risk assessment stats: {}", e);
761 }
762 }
763 }
764
765 fn update_stats_timeout(&self) {
766 match self.stats.safe_lock() {
767 Ok(mut stats) => {
768 stats.timeout_failures += 1;
769 }
770 Err(e) => {
771 tracing::warn!("Failed to update timeout stats: {}", e);
772 }
773 }
774 }
775}
776
777static GLOBAL_ENHANCED_FFI_RESOLVER: std::sync::OnceLock<Arc<EnhancedFfiFunctionResolver>> =
779 std::sync::OnceLock::new();
780
781pub fn get_global_enhanced_ffi_resolver() -> Arc<EnhancedFfiFunctionResolver> {
783 GLOBAL_ENHANCED_FFI_RESOLVER
784 .get_or_init(|| {
785 Arc::new(EnhancedFfiFunctionResolver::new(
786 EnhancedResolverConfig::default(),
787 ))
788 })
789 .clone()
790}
791
792pub fn initialize_global_enhanced_ffi_resolver(
794 config: EnhancedResolverConfig,
795) -> Arc<EnhancedFfiFunctionResolver> {
796 let resolver = Arc::new(EnhancedFfiFunctionResolver::new(config));
797 match GLOBAL_ENHANCED_FFI_RESOLVER.set(resolver.clone()) {
798 Ok(_) => tracing::info!("🔍 Global enhanced FFI function resolver initialized"),
799 Err(_) => tracing::warn!("🔍 Global enhanced FFI function resolver already initialized"),
800 }
801 resolver
802}
803
804#[cfg(test)]
805mod tests {
806 use super::*;
807
808 #[test]
809 fn test_enhanced_resolver_basic() {
810 let resolver = EnhancedFfiFunctionResolver::new(EnhancedResolverConfig::default());
811
812 let result = resolver.resolve_function("malloc", Some("libc"));
813 assert!(result.is_ok());
814
815 let resolved = result.unwrap();
816 assert_eq!(resolved.function_name, "malloc");
817 assert_eq!(resolved.library_name, "libc");
818 assert_eq!(resolved.risk_level, FfiRiskLevel::High);
819 }
820
821 #[test]
822 fn test_pattern_matching() {
823 let resolver = EnhancedFfiFunctionResolver::new(EnhancedResolverConfig::default());
824
825 let result = resolver.resolve_function("pthread_create", None);
826 assert!(result.is_ok());
827
828 let resolved = result.unwrap();
829 assert_eq!(resolved.library_name, "libpthread");
830 assert_eq!(resolved.category, FfiFunctionCategory::ThreadOperation);
831 }
832
833 #[test]
834 fn test_risk_assessment() {
835 let resolver = EnhancedFfiFunctionResolver::new(EnhancedResolverConfig::default());
836
837 let malloc_risk = resolver.assess_function_risk("malloc", "libc");
838 assert_eq!(malloc_risk, FfiRiskLevel::High);
839
840 let strlen_risk = resolver.assess_function_risk("strlen", "libc");
841 assert_eq!(strlen_risk, FfiRiskLevel::Low);
842 }
843
844 #[test]
845 fn test_caching() {
846 let resolver = EnhancedFfiFunctionResolver::new(EnhancedResolverConfig::default());
847
848 let result1 = resolver.resolve_function("test_function", Some("test_lib"));
850 assert!(result1.is_ok());
851
852 let result2 = resolver.resolve_function("test_function", Some("test_lib"));
854 assert!(result2.is_ok());
855
856 let stats = resolver.get_stats().unwrap();
857 assert!(stats.cache_hits > 0);
858 }
859}