This commit is contained in:
Mateusz Gruszczyński committed 2026-10-04 11:15:58 +02:00
1 parent 87e571c170
commit fa7abc9e53
308 files changed
+49746 -50237

No files matched your search

+180
View File
@@ -0,0 +1,180 @@
mod tests {
use super::*;
fn context(settings: Settings, incoming: HeaderMap, peer: &str) -> Context {
settings
.compile()
.unwrap()
.context(
&incoming,
peer.parse().unwrap(),
"10.0.0.1:8443".parse().unwrap(),
true,
&"app.example:8443".parse().unwrap(),
&"/a?x=1&y=2".parse().unwrap(),
)
.unwrap()
}
#[test]
fn untrusted_headers_are_rebuilt_not_appended() {
let mut headers = HeaderMap::new();
for (key, value) in [
("forwarded", "for=1.1.1.1"),
("x-forwarded-for", "2.2.2.2"),
("x-real-ip", "3.3.3.3"),
("x-forwarded-host", "evil.example"),
("x-forwarded-user", "admin"),
("x-original-url", "http://evil/"),
] {
headers.insert(key, value.parse().unwrap());
}
let context = context(Settings::default(), headers.clone(), "192.0.2.5:32000");
context.apply(&mut headers);
assert_eq!(headers["x-forwarded-for"], "192.0.2.5");
assert_eq!(headers["x-real-ip"], "192.0.2.5");
assert_eq!(headers["x-forwarded-host"], "app.example:8443");
assert!(!headers.contains_key("x-forwarded-user"));
assert!(!headers.contains_key("x-original-url"));
assert_eq!(
headers["forwarded"],
"for=192.0.2.5;by=10.0.0.1;proto=https;host=\"app.example:8443\""
);
}
#[test]
fn upstream_can_withhold_client_ip_without_losing_origin_metadata() {
let mut headers = HeaderMap::new();
headers.insert("x-forwarded-for", "203.0.113.99".parse().unwrap());
headers.insert("x-real-ip", "203.0.113.99".parse().unwrap());
headers.insert("forwarded", "for=203.0.113.99".parse().unwrap());
let context = context(Settings::default(), HeaderMap::new(), "192.0.2.5:32000");
context.apply_upstream(&mut headers, false);
assert!(!headers.contains_key("x-forwarded-for"));
assert!(!headers.contains_key("x-real-ip"));
assert!(!headers.contains_key("forwarded"));
assert_eq!(headers["x-forwarded-host"], "app.example:8443");
assert_eq!(headers["x-forwarded-proto"], "https");
assert_eq!(headers["x-forwarded-port"], "8443");
assert_eq!(headers["x-forwarded-uri"], "/a?x=1&y=2");
}
#[test]
fn upstream_client_ip_forwarding_remains_default_behavior() {
let mut headers = HeaderMap::new();
let context = context(Settings::default(), HeaderMap::new(), "192.0.2.5:32000");
context.apply_upstream(&mut headers, true);
assert_eq!(headers["x-forwarded-for"], "192.0.2.5");
assert_eq!(headers["x-real-ip"], "192.0.2.5");
assert!(headers.contains_key("forwarded"));
}
#[test]
fn trust_chain_stops_at_first_untrusted_hop() {
let mut headers = HeaderMap::new();
headers.insert(
"x-forwarded-for",
"6.6.6.6, 198.51.100.7, 10.1.2.3".parse().unwrap(),
);
let c = context(
Settings {
trusted_proxies: vec!["10.0.0.0/8".into()],
..Default::default()
},
headers,
"10.2.3.4:99",
);
assert_eq!(
c.values["x-forwarded-for"],
"198.51.100.7, 10.1.2.3, 10.2.3.4"
);
assert_eq!(c.client_ip.to_string(), "198.51.100.7");
}
#[test]
fn forwarded_only_ipv6_and_duplicate_fields() {
let mut h = HeaderMap::new();
h.append(
"forwarded",
"for=\"[2001:db8::1]:1234\";proto=http".parse().unwrap(),
);
h.append("forwarded", "for=10.1.1.1".parse().unwrap());
let c = context(
Settings {
trusted_proxies: vec!["10.0.0.0/8".into()],
..Default::default()
},
h,
"10.2.2.2:99",
);
assert_eq!(
c.values["x-forwarded-for"],
"2001:db8::1, 10.1.1.1, 10.2.2.2"
);
assert!(c.values["forwarded"]
.to_str()
.unwrap()
.starts_with("for=\"[2001:db8::1]\""));
}
#[test]
fn invalid_and_oversized_chains_fall_back_to_peer() {
for raw in [
"unknown".to_owned(),
"_hidden".into(),
"1.2.3.4, garbage".into(),
vec!["1.2.3.4"; 33].join(","),
] {
let mut h = HeaderMap::new();
h.insert("x-forwarded-for", raw.parse().unwrap());
let c = context(
Settings {
trusted_proxies: vec!["10.0.0.0/8".into()],
..Default::default()
},
h,
"10.2.2.2:99",
);
assert_eq!(c.client_ip.to_string(), "10.2.2.2");
}
}
#[test]
fn proto_trust_is_explicit_and_host_never_comes_from_forwarded() {
let mut h = HeaderMap::new();
h.insert("x-forwarded-proto", "http".parse().unwrap());
h.insert("x-forwarded-host", "evil".parse().unwrap());
let settings = Settings {
trusted_proxies: vec!["10.0.0.0/8".into()],
trust_forwarded_proto: true,
};
let c = context(settings, h, "10.0.0.2:22");
assert_eq!(c.original_url, "http://app.example:8443/a?x=1&y=2");
}
#[test]
fn refuses_trust_all_and_invalid_cidrs() {
for value in ["0.0.0.0/0", "::/0", "broken"] {
assert!(Settings {
trusted_proxies: vec![value.into()],
..Default::default()
}
.compile()
.is_err());
}
}
#[test]
fn cloudflare_header_requires_cloudflare_peer() {
let mut headers = HeaderMap::new();
headers.insert("cf-connecting-ip", "203.0.113.8".parse().unwrap());
assert_eq!(
cloudflare_client_ip(&headers, "104.16.1.2:443".parse().unwrap()),
Some("203.0.113.8".parse().unwrap())
);
assert!(cloudflare_client_ip(&headers, "192.0.2.2:443".parse().unwrap()).is_none());
}
#[test]
fn authenticated_client_override_prepends_chain_once() {
let mut c = context(Settings::default(), HeaderMap::new(), "104.16.1.2:443");
c.override_client_ip("203.0.113.8".parse().unwrap())
.unwrap();
assert_eq!(c.client_ip.to_string(), "203.0.113.8");
assert_eq!(c.values["x-forwarded-for"], "203.0.113.8, 104.16.1.2");
c.override_client_ip("203.0.113.8".parse().unwrap())
.unwrap();
assert_eq!(c.values["x-forwarded-for"], "203.0.113.8, 104.16.1.2");
}
}