You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
harden(transport): gate --allow-host on the real socket peer, not the Host header (#544)
* harden(transport): gate --allow-host on the real socket peer, not the Host header
When --allow-host widens access to a LAN range, the HTTP transport binds
off loopback and authorization previously rested entirely on is_allowed_host,
which range-checks the client-controlled Host header. A peer outside the
allowed range could pass by spoofing `Host: <an-allowed-ip>`; the real peer
address was never consulted (no scope["client"] / remote_address use anywhere).
Add peer_ip_allowed(): the authoritative gate keyed on the unforgeable socket
peer (scope["client"] for ASGI, remote_address for the WebSocket server). When
an allowlist is set, the peer must be loopback or inside an allowed network;
the Host header is kept as a secondary range filter but is no longer the
authority. Fails closed when opted in and the peer can't be determined.
Loopback-only mode (no --allow-host) is a pass-through — the kernel bind
already guarantees a loopback peer — so behavior there is byte-for-byte
unchanged. The WebSocket port stays loopback-only regardless, so its guard
extracts the peer defensively but never gates on it today.
Adds peer_ip_allowed unit coverage plus middleware/evaluate_loopback tests
for the spoofed-Host-from-foreign-peer case and the fail-closed-on-unknown-peer
case. Addresses advisory GHSA-pp3p-fhg5-f429.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* harden(transport): unwrap IPv4-mapped IPv6 peers in the allow-host gate
Copilot review: on a dual-stack listener the real peer can arrive as an
IPv4-mapped IPv6 address (::ffff:127.0.0.1). ipaddress marks that form
.is_loopback == False and it never matches an IPv4 allowlist network, so a
genuine loopback / in-network peer would be wrongly rejected under --allow-host.
Unwrap .ipv4_mapped before the loopback / allowlist checks. Adds regression
coverage for mapped loopback / in-network / out-of-network peers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(transport): reflect the peer gate in the 403 body + WS guard docstring
Copilot re-review: with the new peer-address gate a request can be refused
solely on its peer IP, but the 403 body said only "non-loopback Host or Origin"
and the make_websocket_request_guard docstring said --allow-host only widens the
Host allowlist. Both now mention the unforgeable peer address. The body keeps
the "DNS rebinding" phrasing the existing test pins.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
0 commit comments