- IsPublic now refuses every non-globally-reachable IPv4 special-purpose
range (adds 192.0.0.0/24, TEST-NET-1/2/3, 192.88.99.0/24, benchmarking
198.18.0.0/15) and admits IPv6 only from global unicast 2000::/3, minus
its documentation, 6to4, Teredo and IETF protocol-assignment ranges.
Test fixtures move off TEST-NET-3 to public addresses; Fetch-level tests
cover the new blocked ranges and the public IPv6 path.
- The cache stores and returns copies of Page.Body, so callers cannot
alter cached bytes or race with other readers.
- The per-host rate limiter advances only when a request is let through;
a wait that ends with its context holds no slot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
internal/recipes.Fetcher fetches https:// URLs only. Its DialContext
resolves the host once, refuses unless every answer is public (loopback,
RFC 1918, link-local, CGNAT 100.64/10, multicast, unspecified, reserved,
IPv6 ULA/link-local/multicast and IPv4-mapped or -embedding forms are
blocked) and dials the address it checked, so a rebinding DNS answer
cannot swap in a private IP. Keep-alives and environment proxies are off,
so every request and redirect hop dials and checks afresh.
At most 3 redirects, each re-checked for scheme and address; bodies over
2 MB are an error rather than truncated; 10 s for the whole fetch; a
descriptive User-Agent; requests to one host at least 1 s apart; 2xx
pages cached by URL for an hour.
Tests use a stub resolver and httptest TLS servers.
Maestro task: f4414e41-bd99-498d-a613-a90053963249
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>