Permit private IPv6 while redacting responses

This commit is contained in:
ms2k 2026-07-17 03:45:13 +09:00
commit 5cbb0364d2
4 changed files with 47 additions and 3 deletions

View file

@ -163,7 +163,12 @@ fn is_metadata_capable_space(address: IpAddr) -> bool {
|| (first == 169 && second == 254)
|| (first == 192 && second == 0 && third == 0)
}
IpAddr::V6(address) => address.segments()[0] & 0xfe00 == 0xfc00,
IpAddr::V6(address) => {
let segments = address.segments();
(segments[0] == 0xfd00 && segments[1] == 0x0ec2)
|| (segments[0] == 0xfd20 && segments[1] == 0x00ce)
|| (segments[..4] == [0xfd00, 0x0db8, 0xdeca, 0xdeed])
}
}
}

View file

@ -32,12 +32,20 @@ pub struct OutboundRequest {
credential_headers: HeaderMap,
}
#[derive(Debug)]
pub struct OutboundResponse {
final_url: CanonicalUrl,
response: reqwest::Response,
}
impl fmt::Debug for OutboundResponse {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("OutboundResponse")
.field("status", &self.response.status())
.finish_non_exhaustive()
}
}
impl<R: Resolver> OutboundClient<R> {
#[must_use]
pub fn builder(resolver: R, policy: OutboundPolicy) -> OutboundClientBuilder<R> {

View file

@ -158,11 +158,14 @@ async fn allowlists_never_override_metadata_denial() {
let policy = OutboundPolicyBuilder::new()
.allow_non_global("10.0.0.0/8".parse::<IpNet>().expect("network"))
.allow_non_global("169.254.0.0/16".parse::<IpNet>().expect("network"))
.allow_non_global("fd00::/8".parse::<IpNet>().expect("network"))
.allow_non_global("fc00::/7".parse::<IpNet>().expect("network"))
.build();
authorize_literal(&policy, "10.1.2.3")
.await
.expect("allowlisted private address");
authorize_literal(&policy, "fd12:3456:789a::1")
.await
.expect("allowlisted ULA address");
for address in [
"169.254.169.254",
"169.254.170.2",

View file

@ -251,6 +251,34 @@ fn request_debug_redacts_url_and_credential_values() {
assert!(!debug.contains("synthetic-api-key"));
}
#[tokio::test]
async fn response_debug_redacts_url_and_response_headers() {
let (port, _, server) = serve_http(1, |_| {
response(
"200 OK",
&[("Set-Cookie", "session=synthetic-secret")],
"ok",
)
})
.await;
let resolver = ScriptedResolver::new([("source.test", vec![vec![ip("127.0.0.1")]])]);
let client = OutboundClient::builder(resolver, local_policy(port, 5)).build();
let result = client
.execute(
OutboundRequest::get(&format!(
"http://source.test:{port}/sensitive?token=synthetic"
))
.expect("request"),
)
.await
.expect("response");
let debug = format!("{result:?}");
assert!(debug.contains("200"));
assert!(!debug.contains("sensitive"));
assert!(!debug.contains("synthetic-secret"));
server.await.expect("server");
}
#[tokio::test]
async fn denied_redirect_is_rejected_before_dial() {
let (port, _, server) = serve_http(1, |request| {