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:
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.
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
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
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Where:
internal/httpfetcher/httpfetcher.go,validateURL, onnextat4a4bc97. Found while working on #293.What is wrong:
validateURLtakes the host from the source URL and strips a port withnet.SplitHostPort. A bracketed IPv6 host with no port, such as[2001:db8::a]fromhttps://[2001:db8::a]/a.png, makesSplitHostPortfail, 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:
/v1/image/and through an encrypted URL, and the private-address check still applies to it.make checkgreen.Package:
internal/httpfetcher.Model: opus-5-5
Plan:
validateURLininternal/httpfetcher/httpfetcher.gowill parse the source URL withnet/urland take its host fromHostname(), which drops the brackets of an IPv6 address and any port. Today it cuts off a port withnet.SplitHostPort, which fails when no port follows and leaves the brackets on. The name lookup and the private-address check then see2001:db8::awhether 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
Built as planned in #296. Only
validateURLchanged; the fetcher's other uses of the host needed nothing.Model: opus-5-5