1 Commits
Author SHA1 Message Date
sneak 2670d9002f Take the signature, the expiry and the URL scheme off the image request (closes #314)
check / check (push) Waiting to run
The image request now holds only its source and its transform, exactly what
the variant key covers. The /v1/image/ handler checks the allowlist and
verifies the signature itself with Signer.Verify(req, sig, expires). Both
image handlers keep the URL's expiry as a local value for Cache-Control. The
image service holds no signer, allowlist or AllowHTTP, and ValidateRequest is
gone. The fetcher takes the source and builds its URL with Source.URL from its
own AllowHTTP, so no URL string is built and parsed again. chooseAutoFormat
returns the format it chooses instead of writing into the request. The signed
URL format and every route's answers are unchanged.

Model: opus-5-5
2026-10-26 12:34:19 +00:00
7 changed files with 21 additions and 21 deletions
@@ -75,9 +75,9 @@ func TestFormatForAccept(t *testing.T) {
// autoPhotoURLs returns a signed /v1/image/ URL and an encrypted /v1/e/ URL,
// both valid for a minute, for the JPEG at photoPath on signedHost at 50x50 in
// the format auto. The first is signed with testSigningKey, the key of the
// image service newSignedHostServer builds, and the second is made with h's
// generator.
// the format auto. The first is signed with testSigningKey, the key the
// handlers that newSignedHostServer builds check signatures with. The second
// is made with h's generator.
func autoPhotoURLs(t *testing.T, h *Handlers) (string, string) {
t.Helper()
@@ -67,7 +67,7 @@ func newImageRoute(t *testing.T, fetcher httpfetcher.Fetcher) http.Handler {
}
// newPhotoFetcher returns a mock fetcher that serves a JPEG at photoPath on
// each of hosts, and answers any other URL with an upstream error.
// each of hosts, and answers any other source with an upstream error.
func newPhotoFetcher(t *testing.T, hosts ...string) *httpfetcher.MockFetcher {
t.Helper()
@@ -18,8 +18,8 @@ import (
// signedPhotoURL returns a signed /v1/image/ URL, valid for a minute, for the
// JPEG at photoPath on signedHost at 50x50. It is signed with testSigningKey,
// the key of the image service newSignedHostServer builds. It takes a Handlers
// it does not use, so that it has the type of encPhotoURL.
// the key the handlers that newSignedHostServer builds check signatures with.
// It takes a Handlers it does not use, so that it has the type of encPhotoURL.
func signedPhotoURL(t *testing.T, _ *Handlers) string {
t.Helper()
+2 -1
View File
@@ -133,7 +133,8 @@ func parseImageComponents(remainder string) (*Request, error) {
return nil, fmt.Errorf("%w: %q", ErrInvalidHost, host)
}
// Go's URL parser refuses brackets around anything but an IPv6 address
// The allowlist check and the fetcher drop the brackets, so brackets
// around anything but an IPv6 address would give a host a second spelling
if inBrackets := hostMatch[1]; inBrackets != "" {
addr, err := netip.ParseAddr(inBrackets)
if err != nil || !addr.Is6() {
@@ -79,8 +79,8 @@ func setupHeldFetchService(t *testing.T) (*Service, *TestFixtures, *heldFetcher)
}
// photoVariant asks for the test photo, 100x100, at 50x25 as a JPEG of the
// given quality and fit mode. Each call returns a new request, as Get writes
// to the request it is given.
// given quality and fit mode. Each call returns a new request, which a test
// may change.
func photoVariant(
fixtures *TestFixtures, quality int, fit imagerequest.Fit,
) *imagerequest.Request {
+4 -5
View File
@@ -17,7 +17,7 @@ const (
testPathUpload = "/uploads/image.jpg"
)
func TestService_Get_AllowlistedHost(t *testing.T) {
func TestService_Get_GoodHost(t *testing.T) {
t.Parallel()
svc, fixtures := SetupTestService(t)
@@ -58,10 +58,9 @@ func TestService_Get_AllowlistedHost(t *testing.T) {
}
}
// TestService_Get_NonAllowlistedHost checks that the image service serves a
// host that is not on the allowlist as any other: the /v1/image/ handler checks
// the signature for such a host before it asks the service for the image.
func TestService_Get_NonAllowlistedHost(t *testing.T) {
// TestService_Get_OtherHost checks that the image service serves a second host
// as it serves GoodHost.
func TestService_Get_OtherHost(t *testing.T) {
t.Parallel()
svc, fixtures := SetupTestService(t)
+7 -7
View File
@@ -31,11 +31,11 @@ const (
// TestFixtures contains paths to test files in the mock filesystem.
type TestFixtures struct {
// Valid image files
GoodHostJPEG string // allowlisted host, valid JPEG
GoodHostPNG string // allowlisted host, valid PNG
GoodHostGIF string // allowlisted host, valid GIF
OtherHostJPEG string // non-allowlisted host, valid JPEG
OtherHostPNG string // non-allowlisted host, valid PNG
GoodHostJPEG string // valid JPEG on GoodHost
GoodHostPNG string // valid PNG on GoodHost
GoodHostGIF string // valid GIF on GoodHost
OtherHostJPEG string // valid JPEG on OtherHost
OtherHostPNG string // valid PNG on OtherHost
// Invalid/edge case files
InvalidFile string // file with wrong magic bytes
@@ -43,8 +43,8 @@ type TestFixtures struct {
TextFile string // text file masquerading as image
// Hostnames
GoodHost string // allowlisted hostname
OtherHost string // non-allowlisted hostname
GoodHost string // a host the mock fetcher serves
OtherHost string // a second host the mock fetcher serves
}
// DefaultFixtures returns the standard test fixture paths.