Webhook URL validation must resolve DNS — hostnames pointing at private space are allowed #51

Closed
opened 2026-07-18 05:31:17 +00:00 by claude-bot · 1 comment
Contributor

validate_webhook_url (app/services/webhook_security.py:18-45) blocks literal
private IPs and a static hostname blocklist but never resolves hostnames, so a
DNS name resolving to private/loopback/link-local space passes validation at
both store time (app/api/channels.py:52-57) and send time
(app/services/notifiers/webhook.py). Fix: resolve the host during validation
and again at send; reject when any resolved address is
private/loopback/link-local/reserved; pin the connection to the validated
address (custom transport) so the checked IP is the connected IP. Keep the
existing literal/blocklist checks as the fast path.

Acceptance criteria:

  • Hostname resolving to 10.0.0.0/8 (etc.) rejected at store and send time
  • Connection uses the address that passed validation
  • Tests cover resolve-reject and the still-valid public-host path

Filed from the 2026-07-17 codebase audit (docs/.internal/report-2026-07-17.md), finding F-12.

`validate_webhook_url` (app/services/webhook_security.py:18-45) blocks literal private IPs and a static hostname blocklist but never resolves hostnames, so a DNS name resolving to private/loopback/link-local space passes validation at both store time (app/api/channels.py:52-57) and send time (app/services/notifiers/webhook.py). Fix: resolve the host during validation and again at send; reject when any resolved address is private/loopback/link-local/reserved; pin the connection to the validated address (custom transport) so the checked IP is the connected IP. Keep the existing literal/blocklist checks as the fast path. **Acceptance criteria:** - [ ] Hostname resolving to 10.0.0.0/8 (etc.) rejected at store and send time - [ ] Connection uses the address that passed validation - [ ] Tests cover resolve-reject and the still-valid public-host path --- _Filed from the 2026-07-17 codebase audit (`docs/.internal/report-2026-07-17.md`), finding F-12._
Author
Contributor

Fixed in #93 (merged to main). validate_webhook_url now resolves all A/AAAA records (via socket.getaddrinfo, keeping the sync signature so the store-time call site is unchanged) and rejects any host whose resolved addresses include private/loopback/link-local/reserved/non-global space (IPv4-mapped IPv6 unwrapped first; unresolvable hosts rejected). At send time the generic webhook path pins the connection to the validated IP while preserving the Host header and TLS SNI, closing the DNS-rebinding (resolve-then-connect) gap. Tests cover private-resolution rejection, the still-valid public host, unresolvable hosts, and that the send targets the validated IP.

Fixed in #93 (merged to `main`). `validate_webhook_url` now resolves all A/AAAA records (via `socket.getaddrinfo`, keeping the sync signature so the store-time call site is unchanged) and rejects any host whose resolved addresses include private/loopback/link-local/reserved/non-global space (IPv4-mapped IPv6 unwrapped first; unresolvable hosts rejected). At send time the generic webhook path pins the connection to the validated IP while preserving the `Host` header and TLS SNI, closing the DNS-rebinding (resolve-then-connect) gap. Tests cover private-resolution rejection, the still-valid public host, unresolvable hosts, and that the send targets the validated IP.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/WeatherBot#51
No description provided.