fix: validate connected peer for rotating CDNs
deploy / deploy (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-22 05:56:30 +02:00
parent e234641c36
commit 7d3d026d1d
5 changed files with 112 additions and 27 deletions
+38 -10
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import hashlib
import ipaddress
from collections.abc import Mapping
from dataclasses import dataclass
from datetime import datetime
@@ -53,6 +54,38 @@ class FetchTimeoutError(FetchError):
pass
def _connected_peer_ip(
response: httpx.Response,
) -> ipaddress.IPv4Address | ipaddress.IPv6Address | None:
"""Return the actual remote peer when httpcore exposes its network stream."""
stream = response.extensions.get("network_stream")
if stream is None or not hasattr(stream, "get_extra_info"):
return None
try:
server_address = stream.get_extra_info("server_addr")
raw_address = server_address[0] if server_address else None
return ipaddress.ip_address(raw_address) if raw_address else None
except (IndexError, TypeError, ValueError, OSError):
return None
def _validate_connected_peer(
response: httpx.Response,
*,
before: ValidatedUrl,
after: ValidatedUrl,
) -> None:
peer_ip = _connected_peer_ip(response)
if peer_ip is not None:
if not peer_ip.is_global:
raise FetchError(f"Niet-publiek verbonden IP-adres geblokkeerd: {peer_ip}")
return
# Mock/custom transports do not always expose the peer socket. Keep the conservative
# DNS-overlap fallback for those transports.
if not set(before.addresses).intersection(after.addresses):
raise FetchError("DNS-rebindcontrole faalde bij het benaderen van bron.")
def parse_retry_after(value: str | None) -> int | None:
if not value:
return None
@@ -127,26 +160,22 @@ def fetch_url(
verify=trusted_tls_context(),
)
current_url = url
previous_validation: ValidatedUrl | None = None
try:
for _ in range(settings.FETCHER_MAX_REDIRECTS + 1):
validation = validate_public_url(
current_url,
allow_nonstandard_ports=settings.FETCHER_ALLOW_NONSTANDARD_PORTS,
)
if (
previous_validation is not None
and validation.hostname == previous_validation.hostname
and not set(validation.addresses).intersection(previous_validation.addresses)
):
raise FetchError("DNS-rebindcontrole faalde bij redirect naar dezelfde host.")
response = http_client.get(current_url, headers=headers)
post_validation = validate_public_url(
str(response.url) if response.url else current_url,
allow_nonstandard_ports=settings.FETCHER_ALLOW_NONSTANDARD_PORTS,
)
if not set(validation.addresses).intersection(set(post_validation.addresses)):
raise FetchError("DNS-rebindcontrole faalde bij het benaderen van bron.")
_validate_connected_peer(
response,
before=validation,
after=post_validation,
)
if response.status_code in {301, 302, 303, 307, 308}:
location = response.headers.get("location")
if not location:
@@ -155,7 +184,6 @@ def fetch_url(
next_decision = assess_url(current_url, source=source)
if not next_decision.allowed:
raise PolicyBlockedError(next_decision.reason)
previous_validation = validation
continue
if response.status_code == 304:
return FetchedDocument(url, current_url, 304, dict(response.headers), b"")