Skip to main content

simple_waf_scanner/
config.rs

1use serde::{Deserialize, Serialize};
2
3/// Configuration for WAF scanner
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Config {
6    /// Target URL to scan
7    pub target: String,
8
9    /// Number of concurrent requests
10    pub concurrency: usize,
11
12    /// Delay between requests in milliseconds
13    pub delay_ms: u64,
14
15    /// Custom payload file path (optional)
16    pub payload_file: Option<String>,
17
18    /// Enabled evasion techniques (None = all enabled)
19    pub enabled_techniques: Option<Vec<String>>,
20
21    /// Verbose output mode
22    pub verbose: bool,
23
24    /// LLM-specific testing mode
25    pub llm_mode: bool,
26
27    /// Enable semantic analysis for LLM responses
28    pub semantic_analysis: bool,
29
30    /// Custom User-Agent header
31    pub user_agent: String,
32}
33
34impl Config {
35    /// Create a new configuration with default values
36    pub fn new(target: String) -> Self {
37        Self {
38            target,
39            concurrency: 10,
40            delay_ms: 100,
41            payload_file: None,
42            enabled_techniques: None,
43            verbose: false,
44            llm_mode: false,
45            semantic_analysis: false,
46            user_agent: "Mozilla/5.0 (WAF Scanner)".to_string(),
47        }
48    }
49
50    /// Validate the configuration
51    pub fn validate(&self) -> crate::error::Result<()> {
52        // Check target URL
53        if self.target.is_empty() {
54            return Err(crate::error::ScanError::ConfigError(
55                "Target URL cannot be empty".to_string(),
56            ));
57        }
58
59        if !self.target.starts_with("http://") && !self.target.starts_with("https://") {
60            return Err(crate::error::ScanError::ConfigError(
61                "Target URL must start with http:// or https://".to_string(),
62            ));
63        }
64
65        // Validate URL format
66        if let Err(e) = url::Url::parse(&self.target) {
67            return Err(crate::error::ScanError::ConfigError(
68                format!("Invalid URL format: {}", e),
69            ));
70        }
71
72        // Check concurrency bounds
73        if self.concurrency == 0 {
74            return Err(crate::error::ScanError::ConfigError(
75                "Concurrency must be greater than 0".to_string(),
76            ));
77        }
78
79        if self.concurrency > 100 {
80            return Err(crate::error::ScanError::ConfigError(
81                "Concurrency cannot exceed 100 (too aggressive)".to_string(),
82            ));
83        }
84
85        // Check delay
86        if self.delay_ms > 10000 {
87            return Err(crate::error::ScanError::ConfigError(
88                "Delay cannot exceed 10 seconds".to_string(),
89            ));
90        }
91
92        // Validate payload file if provided
93        if let Some(ref path) = self.payload_file {
94            if !std::path::Path::new(path).exists() {
95                return Err(crate::error::ScanError::ConfigError(
96                    format!("Payload file not found: {}", path),
97                ));
98            }
99        }
100
101        Ok(())
102    }
103}
104
105impl Default for Config {
106    fn default() -> Self {
107        Self::new(String::new())
108    }
109}