An IPv6 source host written without a port is refused by the fetcher and answers 500 #295

Closed
opened 2026-10-16 03:48:23 +02:00 by clawbot · 2 comments
Collaborator

Where: internal/httpfetcher/httpfetcher.go, validateURL, on next at 4a4bc97. Found while working on #293.

What is wrong: validateURL takes the host from the source URL and strips a port with net.SplitHostPort. A bracketed IPv6 host with no port, such as [2001:db8::a] from https://[2001:db8::a]/a.png, makes SplitHostPort fail, so the host keeps its brackets, the name lookup of [2001:db8::a] fails, and the fetch is refused as an invalid host. An encrypted URL for such a source answered 500; an image URL through /v1/image/ with such a host, which #275 accepts, very likely fails the same way. With a port ([2001:db8::a]:8443) it works.

Severity: low

Definition of done:

  1. A source URL whose host is a bracketed IPv6 address without a port is fetched like one with a port, through /v1/image/ and through an encrypted URL, and the private-address check still applies to it.
  2. Router tests first, against a test upstream that records the request; make check green.

Package: internal/httpfetcher.

Model: opus-5-5

Where: `internal/httpfetcher/httpfetcher.go`, `validateURL`, on `next` at `4a4bc97`. Found while working on https://git.eeqj.de/sneak/pixa/issues/293. What is wrong: `validateURL` takes the host from the source URL and strips a port with `net.SplitHostPort`. A bracketed IPv6 host with no port, such as `[2001:db8::a]` from `https://[2001:db8::a]/a.png`, makes `SplitHostPort` fail, so the host keeps its brackets, the name lookup of `[2001:db8::a]` fails, and the fetch is refused as an invalid host. An encrypted URL for such a source answered 500; an image URL through `/v1/image/` with such a host, which https://git.eeqj.de/sneak/pixa/issues/275 accepts, very likely fails the same way. With a port (`[2001:db8::a]:8443`) it works. Severity: low Definition of done: 1. A source URL whose host is a bracketed IPv6 address without a port is fetched like one with a port, through `/v1/image/` and through an encrypted URL, and the private-address check still applies to it. 2. Router tests first, against a test upstream that records the request; `make check` green. Package: `internal/httpfetcher`. Model: opus-5-5
clawbot self-assigned this 2026-10-16 03:48:39 +02:00
Author
Collaborator

Plan: validateURL in internal/httpfetcher/httpfetcher.go will parse the source URL with net/url and take its host from Hostname(), which drops the brackets of an IPv6 address and any port. Today it cuts off a port with net.SplitHostPort, which fails when no port follows and leaves the brackets on. The name lookup and the private-address check then see 2001:db8::a whether or not a port follows it. Elsewhere in the fetcher the host is read either from an address that always has a port (the dialer) or only to count connections per host, so nothing else changes.

Router tests come first, against a test upstream that records each request it gets. They cover an IPv6 source host with and without a port, through a /v1/image/ URL with a signature and through an encrypted URL, and check that the image is fetched. A private IPv6 address with and without a port must be refused with 403 and reach no upstream.

Model: opus-5-5

Plan: `validateURL` in `internal/httpfetcher/httpfetcher.go` will parse the source URL with `net/url` and take its host from `Hostname()`, which drops the brackets of an IPv6 address and any port. Today it cuts off a port with `net.SplitHostPort`, which fails when no port follows and leaves the brackets on. The name lookup and the private-address check then see `2001:db8::a` whether or not a port follows it. Elsewhere in the fetcher the host is read either from an address that always has a port (the dialer) or only to count connections per host, so nothing else changes. Router tests come first, against a test upstream that records each request it gets. They cover an IPv6 source host with and without a port, through a `/v1/image/` URL with a signature and through an encrypted URL, and check that the image is fetched. A private IPv6 address with and without a port must be refused with 403 and reach no upstream. Model: opus-5-5
Author
Collaborator

Built as planned in #296. Only validateURL changed; the fetcher's other uses of the host needed nothing.

Model: opus-5-5

Built as planned in https://git.eeqj.de/sneak/pixa/pulls/296. Only `validateURL` changed; the fetcher's other uses of the host needed nothing. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#295