kindly_guard_server/scanner/
sync_wrapper.rs1use super::{ScanError, SecurityScanner, Threat};
20use crate::config::ScannerConfig;
21use std::sync::Arc;
22
23pub struct SyncSecurityScanner {
28 scanner: Arc<SecurityScanner>,
29 #[allow(dead_code)] runtime: tokio::runtime::Runtime,
31}
32
33impl SyncSecurityScanner {
34 pub fn new(config: ScannerConfig) -> Result<Self, ScanError> {
36 let runtime = tokio::runtime::Runtime::new()
37 .map_err(|e| ScanError::InvalidInput(format!("Failed to create runtime: {}", e)))?;
38
39 let scanner = Arc::new(SecurityScanner::new(config)?);
40
41 Ok(Self { scanner, runtime })
42 }
43
44 pub fn scan_text(&self, text: &str) -> Result<Vec<Threat>, ScanError> {
46 let mut threats = Vec::new();
48
49 if self.scanner.config.unicode_detection {
50 threats.extend(self.scanner.unicode_scanner.scan_text(text)?);
51 }
52
53 if self.scanner.config.injection_detection {
54 threats.extend(self.scanner.injection_scanner.scan_text(text)?);
55 }
56
57 Ok(threats)
61 }
62
63 pub fn scan_json(&self, value: &serde_json::Value) -> Result<Vec<Threat>, ScanError> {
65 let json_str = serde_json::to_string(value)
67 .map_err(|e| ScanError::InvalidInput(format!("Invalid JSON: {}", e)))?;
68
69 self.scan_text(&json_str)
70 }
71}
72
73pub fn create_sync_scanner(config: ScannerConfig) -> Result<SecurityScanner, ScanError> {
77 let mut sync_config = config;
78 sync_config.xss_detection = Some(false); SecurityScanner::new(sync_config)
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::scanner::ThreatType;
86
87 #[test]
88 fn test_sync_scanner_basic() {
89 let config = ScannerConfig {
90 unicode_detection: true,
91 injection_detection: true,
92 path_traversal_detection: true,
93 xss_detection: Some(false),
94 crypto_detection: true,
95 enhanced_mode: Some(false),
96 custom_patterns: None,
97 max_scan_depth: 10,
98 enable_event_buffer: false,
99 max_content_size: 5 * 1024 * 1024, max_input_size: None,
101 allow_text_control_chars: false,
102 };
103 let scanner = SyncSecurityScanner::new(config).unwrap();
104
105 let threats = scanner
106 .scan_text("SELECT * FROM users WHERE id = '1' OR '1'='1'")
107 .unwrap();
108 assert!(!threats.is_empty());
109 assert!(threats
110 .iter()
111 .any(|t| matches!(t.threat_type, ThreatType::SqlInjection)));
112 }
113
114 #[test]
115 fn test_sync_scanner_unicode() {
116 let config = ScannerConfig {
117 unicode_detection: true,
118 injection_detection: true,
119 path_traversal_detection: true,
120 xss_detection: Some(false),
121 crypto_detection: true,
122 enhanced_mode: Some(false),
123 custom_patterns: None,
124 max_scan_depth: 10,
125 enable_event_buffer: false,
126 max_content_size: 5 * 1024 * 1024, max_input_size: None,
128 allow_text_control_chars: false,
129 };
130 let scanner = SyncSecurityScanner::new(config).unwrap();
131
132 let threats = scanner.scan_text("Hello\u{202E}World").unwrap();
133 assert!(!threats.is_empty());
134 assert!(threats
135 .iter()
136 .any(|t| matches!(t.threat_type, ThreatType::UnicodeBiDi)));
137 }
138
139 #[test]
140 fn test_create_sync_scanner() {
141 let config = ScannerConfig {
142 unicode_detection: true,
143 injection_detection: true,
144 path_traversal_detection: true,
145 xss_detection: Some(false),
146 crypto_detection: true,
147 enhanced_mode: Some(false),
148 custom_patterns: None,
149 max_scan_depth: 10,
150 enable_event_buffer: false,
151 max_content_size: 5 * 1024 * 1024, max_input_size: None,
153 allow_text_control_chars: false,
154 };
155 let scanner = create_sync_scanner(config).unwrap();
156
157 let threats = scanner.scan_text("'; DROP TABLE users; --").unwrap();
159 assert!(!threats.is_empty());
160 }
161}