From 589318db9706cf5e5d5f3105f7d8d2a673660290 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 15 Oct 2026 05:28:07 +0000 Subject: [PATCH] Read the source host's port from the parsed URL (part of #246) net.SplitHostPort fails on an IPv6 address written without brackets, so with 2001:db8::1 allowlisted, 2001:db8::1:8443 and 2001:db8::1: counted as having no port and were served without a signature, though the fetcher connects to port 8443. ValidateRequest now takes a host to have a port when the parsed URL's Port() is set or the host ends in ":", as Go's URL parser and so the fetcher read it. Model: opus-5-5 --- internal/imgcache/service.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/imgcache/service.go b/internal/imgcache/service.go index c7706c5..01f0db3 100644 --- a/internal/imgcache/service.go +++ b/internal/imgcache/service.go @@ -7,9 +7,9 @@ import ( "fmt" "io" "log/slog" - "net" "net/url" "runtime/debug" + "strings" "time" "github.com/dustin/go-humanize" @@ -220,11 +220,13 @@ func (s *Service) ValidateRequest(req *ImageRequest) error { } // An allowlist entry names no port, so a host followed by ":", with or - // without a port, needs a signature. This is checked here and not in - // allowlist.IsAllowed, which also matches referer_blocklist, where a - // Referer must be blocked whatever its port. - _, _, splitErr := net.SplitHostPort(parsedURL.Host) - hostHasPort := splitErr == nil + // without a port, needs a signature. The port is read from the parsed + // URL, as the fetcher reads it: in an IPv6 address written without + // brackets, such as 2001:db8::1:8443, it is what follows the last ":". + // This is checked here and not in allowlist.IsAllowed, which also matches + // referer_blocklist, where a Referer must be blocked whatever its port. + hostHasPort := parsedURL.Port() != "" || + strings.HasSuffix(parsedURL.Host, ":") if !hostHasPort && s.allowlist.IsAllowed(parsedURL) { return nil