use crate::http_client::HttpResponse;
use crate::types::{Confidence, Severity, Vulnerability};
use chrono::Utc;
pub struct VulnerabilityDetector;
impl VulnerabilityDetector {
pub fn new() -> Self {
Self
}
pub fn detect_xss(
&self,
url: &str,
parameter: &str,
payload: &str,
response: &HttpResponse,
) -> Option<Vulnerability> {
if !response.contains(payload) {
return None;
}
let has_script_tag = response.contains("<script") || response.contains("</script>");
let has_event_handler = response.contains("onerror=") || response.contains("onload=");
let has_javascript_protocol = response.contains("javascript:");
let (severity, confidence) =
if has_script_tag || has_event_handler || has_javascript_protocol {
(Severity::High, Confidence::High)
} else {
(Severity::Medium, Confidence::Medium)
};
Some(Vulnerability {
id: format!("xss_{}", uuid::Uuid::new_v4().to_string()),
vuln_type: "Cross-Site Scripting (XSS)".to_string(),
severity,
confidence,
category: "Injection".to_string(),
url: url.to_string(),
parameter: Some(parameter.to_string()),
payload: payload.to_string(),
description: format!(
"Reflected XSS vulnerability detected in parameter '{}'. The application reflects user input without proper sanitization.",
parameter
),
evidence: Some(format!(
"Payload '{}' was reflected in the response",
payload
)),
cwe: "CWE-79".to_string(),
cvss: 7.5,
verified: true,
false_positive: false,
remediation: "1. Implement output encoding/escaping for all user input\n2. Use Content Security Policy (CSP) headers\n3. Enable X-XSS-Protection header\n4. Validate and sanitize all input server-side".to_string(),
discovered_at: Utc::now().to_rfc3339(),
ml_data: None,
})
}
pub fn detect_sqli(
&self,
url: &str,
parameter: &str,
payload: &str,
response: &HttpResponse,
baseline_response: &HttpResponse,
) -> Option<Vulnerability> {
let sql_errors = vec![
"sql syntax",
"mysql_fetch",
"ora-",
"postgresql",
"microsoft sql server",
"sqlite",
"syntax error",
"warning: mysql",
"pg_query",
"mysqli",
"sqlstate",
"sql server",
"oledbexception",
"sqlexception",
"pdoexception",
"unclosed quotation mark",
"quoted string not properly terminated",
"unterminated quoted string",
"you have an error in your sql",
"mysql error",
"database error",
"query failed",
"sql error",
"invalid query",
"unexpected end of sql",
"near \"", "at line ", "column count", "unknown column",
"table doesn't exist",
"no such table",
"incorrect syntax near",
"division by zero",
];
let response_lower = response.body.to_lowercase();
let baseline_lower = baseline_response.body.to_lowercase();
let has_new_sql_error = sql_errors.iter().any(|pattern| {
response_lower.contains(pattern) && !baseline_lower.contains(pattern)
});
if !has_new_sql_error {
return None;
}
let detected_error = sql_errors
.iter()
.find(|pattern| {
response_lower.contains(*pattern) && !baseline_lower.contains(*pattern)
})
.map(|s| s.to_string())
.unwrap_or_else(|| "SQL error pattern".to_string());
Some(Vulnerability {
id: format!("sqli_{}", uuid::Uuid::new_v4().to_string()),
vuln_type: "SQL Injection".to_string(),
severity: Severity::Critical,
confidence: Confidence::High,
category: "Injection".to_string(),
url: url.to_string(),
parameter: Some(parameter.to_string()),
payload: payload.to_string(),
description: format!(
"SQL injection vulnerability detected in parameter '{}'. The application does not properly sanitize database queries.",
parameter
),
evidence: Some(format!("SQL error detected in response: '{}'", detected_error)),
cwe: "CWE-89".to_string(),
cvss: 9.8,
verified: true,
false_positive: false,
remediation: "1. Use parameterized queries/prepared statements\n2. Implement input validation and sanitization\n3. Apply principle of least privilege for database accounts\n4. Use an ORM framework".to_string(),
discovered_at: Utc::now().to_rfc3339(),
ml_data: None,
})
}
pub fn detect_command_injection(
&self,
url: &str,
parameter: &str,
payload: &str,
response: &HttpResponse,
) -> Option<Vulnerability> {
let command_indicators = vec![
"root:",
"daemon:",
"/bin/",
"/usr/bin/",
"uid=",
"gid=",
"groups=",
];
let has_command_output = command_indicators
.iter()
.any(|pattern| response.body.contains(pattern));
if !has_command_output {
return None;
}
Some(Vulnerability {
id: format!("cmdi_{}", uuid::Uuid::new_v4().to_string()),
vuln_type: "Command Injection".to_string(),
severity: Severity::Critical,
confidence: Confidence::High,
category: "Injection".to_string(),
url: url.to_string(),
parameter: Some(parameter.to_string()),
payload: payload.to_string(),
description: format!(
"Command injection vulnerability detected in parameter '{}'. The application allows execution of arbitrary system commands.",
parameter
),
evidence: Some("System command output detected in response".to_string()),
cwe: "CWE-78".to_string(),
cvss: 9.8,
verified: true,
false_positive: false,
remediation: "1. Avoid executing system commands with user input\n2. Use parameterized APIs when possible\n3. Implement strict input validation\n4. Use allowlists for permitted commands".to_string(),
discovered_at: Utc::now().to_rfc3339(),
ml_data: None,
})
}
}
mod uuid {
use rand::Rng;
pub struct Uuid;
impl Uuid {
pub fn new_v4() -> Self {
Self
}
pub fn to_string(&self) -> String {
let mut rng = rand::rng();
format!(
"{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
rng.random::<u32>(),
rng.random::<u16>(),
rng.random::<u16>(),
rng.random::<u16>(),
rng.random::<u64>() & 0xffffffffffff
)
}
}
}