use crate::{TechCategory, Technology};
#[must_use]
pub fn detect(status: u16, headers: &[(String, String)], body: &[u8]) -> Option<Technology> {
let detected = wafrift_detect::detect(status, headers, body)
.into_iter()
.next()?;
Some(Technology {
name: detected.name.to_string(),
version: None,
category: TechCategory::Security,
confidence: (detected.confidence * 100.0).round().min(100.0) as u8,
})
}
#[must_use]
pub fn supported_wafs() -> Vec<String> {
wafrift_detect::supported_wafs()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cloudflare_waf_detected() {
let headers = vec![
("server".to_string(), "cloudflare".to_string()),
("cf-ray".to_string(), "abc123".to_string()),
];
let result = detect(403, &headers, b"cloudflare ray id");
assert!(result.is_some(), "should detect Cloudflare WAF");
let tech = result.unwrap();
assert_eq!(tech.name, "Cloudflare");
assert_eq!(tech.category, TechCategory::Security);
assert!(tech.confidence > 50);
}
#[test]
fn no_waf_returns_none() {
let headers = vec![("server".to_string(), "nginx/1.21.0".to_string())];
let result = detect(200, &headers, b"<html>hello</html>");
assert!(result.is_none());
}
#[test]
fn supported_wafs_nonempty() {
let wafs = supported_wafs();
assert!(
wafs.len() >= 15,
"should have at least 15 WAF detectors, got {}",
wafs.len()
);
}
}