diff --git a/TODO.md b/TODO.md index a00b1ef..c071ef3 100644 --- a/TODO.md +++ b/TODO.md @@ -30,6 +30,12 @@ P2: security: per-origin rate limiting # Completed Steps +- 2026-10-16 a source host that is an IPv6 address in brackets with no port, + such as `[2001:db8::a]`, is fetched like one with a port, through `/v1/image/` + and through an encrypted URL (closes #295). The fetcher's check of the source + URL kept the brackets when no port followed them, could not look the host up, + and the request answered 500. It now checks the address without brackets, so a + private one is refused with 403, as with a port. - 2026-10-16 a size with one side `0` keeps the side computed from the source's proportions at 1 or more (closes #283): it was rounded down, so for a thin source it came out at 0, and libvips then made the size square. An 8192x1 diff --git a/internal/httpfetcher/httpfetcher.go b/internal/httpfetcher/httpfetcher.go index 1428040..159ee68 100644 --- a/internal/httpfetcher/httpfetcher.go +++ b/internal/httpfetcher/httpfetcher.go @@ -473,16 +473,16 @@ func validateURL(ctx context.Context, rawURL string, allowHTTP bool) error { return ErrUnsupportedScheme } - // Parse to extract host - host := extractHost(rawURL) - if host == "" { - return ErrInvalidHost + parsedURL, err := neturl.Parse(rawURL) + if err != nil { + return fmt.Errorf("%w: %w", ErrInvalidHost, err) } - // Remove port if present - h, _, err := net.SplitHostPort(host) - if err == nil { - host = h + // Hostname drops any port, and the brackets of an IPv6 address whether + // or not a port follows them + host := parsedURL.Hostname() + if host == "" { + return ErrInvalidHost } // Block obvious localhost patterns