use super::{ScanError, SecurityScanner, Threat};
use crate::config::ScannerConfig;
use std::sync::Arc;
pub struct SyncSecurityScanner {
scanner: Arc<SecurityScanner>,
#[allow(dead_code)] runtime: tokio::runtime::Runtime,
}
impl SyncSecurityScanner {
pub fn new(config: ScannerConfig) -> Result<Self, ScanError> {
let runtime = tokio::runtime::Runtime::new()
.map_err(|e| ScanError::InvalidInput(format!("Failed to create runtime: {}", e)))?;
let scanner = Arc::new(SecurityScanner::new(config)?);
Ok(Self { scanner, runtime })
}
pub fn scan_text(&self, text: &str) -> Result<Vec<Threat>, ScanError> {
let mut threats = Vec::new();
if self.scanner.config.unicode_detection {
threats.extend(self.scanner.unicode_scanner.scan_text(text)?);
}
if self.scanner.config.injection_detection {
threats.extend(self.scanner.injection_scanner.scan_text(text)?);
}
Ok(threats)
}
pub fn scan_json(&self, value: &serde_json::Value) -> Result<Vec<Threat>, ScanError> {
let json_str = serde_json::to_string(value)
.map_err(|e| ScanError::InvalidInput(format!("Invalid JSON: {}", e)))?;
self.scan_text(&json_str)
}
}
pub fn create_sync_scanner(config: ScannerConfig) -> Result<SecurityScanner, ScanError> {
let mut sync_config = config;
sync_config.xss_detection = Some(false); SecurityScanner::new(sync_config)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scanner::ThreatType;
#[test]
fn test_sync_scanner_basic() {
let config = ScannerConfig {
unicode_detection: true,
injection_detection: true,
path_traversal_detection: true,
xss_detection: Some(false),
crypto_detection: true,
enhanced_mode: Some(false),
custom_patterns: None,
max_scan_depth: 10,
enable_event_buffer: false,
max_content_size: 5 * 1024 * 1024, max_input_size: None,
allow_text_control_chars: false,
};
let scanner = SyncSecurityScanner::new(config).unwrap();
let threats = scanner
.scan_text("SELECT * FROM users WHERE id = '1' OR '1'='1'")
.unwrap();
assert!(!threats.is_empty());
assert!(threats
.iter()
.any(|t| matches!(t.threat_type, ThreatType::SqlInjection)));
}
#[test]
fn test_sync_scanner_unicode() {
let config = ScannerConfig {
unicode_detection: true,
injection_detection: true,
path_traversal_detection: true,
xss_detection: Some(false),
crypto_detection: true,
enhanced_mode: Some(false),
custom_patterns: None,
max_scan_depth: 10,
enable_event_buffer: false,
max_content_size: 5 * 1024 * 1024, max_input_size: None,
allow_text_control_chars: false,
};
let scanner = SyncSecurityScanner::new(config).unwrap();
let threats = scanner.scan_text("Hello\u{202E}World").unwrap();
assert!(!threats.is_empty());
assert!(threats
.iter()
.any(|t| matches!(t.threat_type, ThreatType::UnicodeBiDi)));
}
#[test]
fn test_create_sync_scanner() {
let config = ScannerConfig {
unicode_detection: true,
injection_detection: true,
path_traversal_detection: true,
xss_detection: Some(false),
crypto_detection: true,
enhanced_mode: Some(false),
custom_patterns: None,
max_scan_depth: 10,
enable_event_buffer: false,
max_content_size: 5 * 1024 * 1024, max_input_size: None,
allow_text_control_chars: false,
};
let scanner = create_sync_scanner(config).unwrap();
let threats = scanner.scan_text("'; DROP TABLE users; --").unwrap();
assert!(!threats.is_empty());
}
}