use crate::waf_detect::detect;
fn first_name(status: u16, headers: &[(String, String)], body: &[u8]) -> Option<String> {
detect(status, headers, body)
.first()
.map(|w| w.name.clone())
}
#[test]
fn detect_cloudflare_from_headers() {
let headers = vec![
("cf-ray".into(), "abc123-IAD".into()),
("server".into(), "cloudflare".into()),
];
let result = first_name(403, &headers, b"Access denied");
assert_eq!(result.as_deref(), Some("Cloudflare"));
}
#[test]
fn detect_major_wafs_from_headers_alone() {
let cases = [
(
"Cloudflare",
vec![
("cf-ray".to_string(), "abc123-IAD".to_string()),
("server".to_string(), "cloudflare".to_string()),
],
),
(
"Kona SiteDefender",
vec![
("x-akamai-transformed".to_string(), "9 12345".to_string()),
("server".to_string(), "akamaighost".to_string()),
],
),
(
"AWS Elastic Load Balancer",
vec![("x-amz-id".to_string(), "BLOCK".to_string())],
),
(
"Incapsula",
vec![
("x-iinfo".to_string(), "10-12345678-0 0NNN RT(0".to_string()),
("x-cdn".to_string(), "Incapsula".to_string()),
],
),
(
"ModSecurity",
vec![("server".to_string(), "Mod_Security".to_string())],
),
];
for (expected, headers) in cases {
let name =
first_name(403, &headers, b"").unwrap_or_else(|| panic!("should detect {expected}"));
assert_eq!(name, expected);
}
}
#[test]
fn detect_aws_from_body() {
let headers = vec![];
let result = first_name(403, &headers, b"<html>Request blocked by AWS WAF</html>");
assert_eq!(result.as_deref(), Some("AWS Elastic Load Balancer"));
}
#[test]
fn detect_akamai_reference() {
let headers = vec![];
let result = first_name(403, &headers, b"Access Denied. Reference #18.abc123def.456");
assert_eq!(result.as_deref(), Some("Kona SiteDefender"));
}
#[test]
fn detect_imperva_cookie() {
let headers = vec![("set-cookie".into(), "visid_incap_123=abc; path=/".into())];
let result = first_name(200, &headers, b"OK");
assert_eq!(result.as_deref(), Some("Incapsula"));
}
#[test]
fn no_waf_on_clean_response() {
let headers = vec![("server".into(), "nginx".into())];
let result = detect(200, &headers, b"<html>Welcome</html>");
assert!(result.is_empty());
}
#[test]
fn detect_f5_bigip() {
let headers = vec![("server".into(), "bigip".into())];
let result = first_name(200, &headers, b"OK");
assert_eq!(result.as_deref(), Some("BIG-IP AP Manager"));
}
#[test]
fn highest_confidence_wins() {
let headers = vec![
("cf-ray".into(), "abc".into()),
("server".into(), "cloudflare".into()),
("x-amz-requestid".into(), "123".into()),
];
let result = first_name(403, &headers, b"blocked");
assert_eq!(result.as_deref(), Some("Cloudflare"));
}
#[test]
fn detect_barracuda() {
let headers = vec![("set-cookie".into(), "barra_counter_session=abc".into())];
let result = first_name(403, &headers, b"Blocked by Barracuda");
assert_eq!(result.as_deref(), Some("Barracuda"));
}
#[test]
fn detect_fortiweb_cookie() {
let headers = vec![("set-cookie".into(), "fortiwafsid=abc123".into())];
let result = first_name(403, &headers, b"Blocked");
assert_eq!(result.as_deref(), Some("FortiWeb"));
}
#[test]
fn detect_wordfence_body() {
let result = first_name(403, &[], b"This response was generated by Wordfence.");
assert_eq!(result.as_deref(), Some("Wordfence"));
}
#[test]
fn modsecurity_406_pattern() {
let result = first_name(406, &[], b"Not Acceptable. ModSecurity blocked the request");
assert_eq!(result.as_deref(), Some("ModSecurity"));
}
#[test]
fn ambiguity_returns_multiple() {
let headers = vec![
("server".into(), "cloudflare".into()),
("x-amz-id".into(), "123".into()),
];
let results = detect(403, &headers, b"blocked");
assert!(!results.is_empty());
}