use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub target: String,
pub concurrency: usize,
pub delay_ms: u64,
pub payload_file: Option<String>,
pub enabled_techniques: Option<Vec<String>>,
pub verbose: bool,
pub llm_mode: bool,
pub semantic_analysis: bool,
pub user_agent: String,
}
impl Config {
pub fn new(target: String) -> Self {
Self {
target,
concurrency: 10,
delay_ms: 100,
payload_file: None,
enabled_techniques: None,
verbose: false,
llm_mode: false,
semantic_analysis: false,
user_agent: "Mozilla/5.0 (WAF Scanner)".to_string(),
}
}
pub fn validate(&self) -> crate::error::Result<()> {
if self.target.is_empty() {
return Err(crate::error::ScanError::ConfigError(
"Target URL cannot be empty".to_string(),
));
}
if !self.target.starts_with("http://") && !self.target.starts_with("https://") {
return Err(crate::error::ScanError::ConfigError(
"Target URL must start with http:// or https://".to_string(),
));
}
if let Err(e) = url::Url::parse(&self.target) {
return Err(crate::error::ScanError::ConfigError(
format!("Invalid URL format: {}", e),
));
}
if self.concurrency == 0 {
return Err(crate::error::ScanError::ConfigError(
"Concurrency must be greater than 0".to_string(),
));
}
if self.concurrency > 100 {
return Err(crate::error::ScanError::ConfigError(
"Concurrency cannot exceed 100 (too aggressive)".to_string(),
));
}
if self.delay_ms > 10000 {
return Err(crate::error::ScanError::ConfigError(
"Delay cannot exceed 10 seconds".to_string(),
));
}
if let Some(ref path) = self.payload_file {
if !std::path::Path::new(path).exists() {
return Err(crate::error::ScanError::ConfigError(
format!("Payload file not found: {}", path),
));
}
}
Ok(())
}
}
impl Default for Config {
fn default() -> Self {
Self::new(String::new())
}
}