Files
at-container-registry/pkg
Evan JarrettandClaude Opus 5 39919cc832 appview: stop webhooks reaching private addresses
The URL check accepted http:// while telling the user "must be https", and
guarded no addresses at all. POST /api/webhooks with http://127.0.0.1:9/hook
returned 200 and created the webhook, so both scheduled deliveries and the
synchronous Test button would dial arbitrary destinations from the appview
host, on demand, for any authenticated user. Loopback, link-local (including
the cloud metadata endpoint at 169.254.169.254) and RFC1918 were all reachable.

Enforces https, and refuses non-public destinations.

The load-bearing half is the dial-time check, not the creation-time one. An
attacker controls their own DNS, so a hostname that resolves publicly when the
webhook is created can resolve to loopback when it is delivered, and a
creation-time check cannot see a redirect either. The guard is therefore a
net.Dialer Control hook on the delivery client, which inspects the resolved
address on every connection attempt. Transport.Proxy is explicitly nil:
honouring HTTP(S)_PROXY would route around the Control hook and hand the
bypass straight back. Redirects are re-validated per hop and capped at 3.

The creation-time check stays so the user gets an immediate, comprehensible
error instead of a silent delivery failure later.

IPv4-mapped IPv6 is unmapped before every check, so ::ffff:127.0.0.1 and
friends hit the IPv4 rules. Ranges with no net.IP helper are listed explicitly:
CGNAT, NAT64, ::/96, TEST-NET and reserved space.

Both outbound paths are covered, since the scheduled dispatcher and the Test
button both funnel through attemptDelivery. The dispatcher's other client is
deliberately left unguarded: it fetches quota stats from holds, which
legitimately live on private addresses, and those URLs are not user-supplied.

Note this removes the ability to point a webhook at a localhost receiver in
local development. There is deliberately no environment-variable escape hatch,
since a security toggle read from the environment is the same bypass wearing a
nicer coat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PDqoCE1j3njokkZ9b1C5n9
2026-09-02 21:37:19 -05:00
..
2026-05-11 19:53:13 -05:00