check / check (push) Successful in 3m6s
The encrypted /v1/e/ route used the decrypted payload unchecked, so a token could request an over-limit size or an unknown fit mode; the generator turned unparseable numbers into 0. imgcache.ValidateDimension alone holds the MaxDimension bound and is used by the path parser, by the new ValidateImageRequest (which adds ValidateFitMode) and by the generator. Both image routes call ValidateImageRequest, so each answers 400. The generator answers 400 naming the field for a width or height that is not a number or fails that check, a quality that is not a number from 1 to 100, a ttl that is not a number from 0 to the largest the expiry calculation can hold, or an unknown fit. Empty quality is 85; empty ttl never expires. The form's size inputs stop at 8192. Model: opus-4-8 (implementation); opus-5-5 (rework)
99 lines
2.6 KiB
Go
99 lines
2.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"sneak.berlin/go/pixa/internal/encurl"
|
|
"sneak.berlin/go/pixa/internal/imgcache"
|
|
)
|
|
|
|
// newEncTestServer builds a router serving the encrypted-URL route with a
|
|
// generator seeded by the shared test signing key. The image service is left
|
|
// nil: these tests exercise validation that rejects a token before any image
|
|
// is fetched, so the handler must never reach the service.
|
|
func newEncTestServer(t *testing.T) (*encurl.Generator, http.Handler) {
|
|
t.Helper()
|
|
|
|
encGen, err := encurl.NewGenerator(testSigningKey)
|
|
if err != nil {
|
|
t.Fatalf("encurl.NewGenerator() error = %v", err)
|
|
}
|
|
|
|
h := &Handlers{
|
|
log: slog.New(slog.DiscardHandler),
|
|
encGen: encGen,
|
|
}
|
|
|
|
r := chi.NewRouter()
|
|
r.Get("/v1/e/{token}/*", h.HandleImageEnc())
|
|
|
|
return encGen, r
|
|
}
|
|
|
|
// getEncToken issues a GET for the given token and returns the recorder.
|
|
func getEncToken(srv http.Handler, token string) *httptest.ResponseRecorder {
|
|
req := httptest.NewRequestWithContext(
|
|
context.Background(), http.MethodGet, "/v1/e/"+token+"/img.jpg", nil)
|
|
rec := httptest.NewRecorder()
|
|
srv.ServeHTTP(rec, req)
|
|
|
|
return rec
|
|
}
|
|
|
|
// TestHandleImageEnc_OverLimitDimension_Returns400 verifies that a decrypted
|
|
// token requesting a dimension beyond MaxDimension is rejected with 400
|
|
// instead of reaching the image processor and libvips.
|
|
func TestHandleImageEnc_OverLimitDimension_Returns400(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
encGen, srv := newEncTestServer(t)
|
|
|
|
token, err := encGen.Generate(&encurl.Payload{
|
|
SourceHost: "cdn.example.com",
|
|
SourcePath: "/photo.jpg",
|
|
Width: 100000,
|
|
Height: 100000,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Generate() error = %v", err)
|
|
}
|
|
|
|
rec := getEncToken(srv, token)
|
|
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadRequest)
|
|
}
|
|
}
|
|
|
|
// TestHandleImageEnc_InvalidFitMode_Returns400 verifies that a decrypted token
|
|
// carrying an unrecognized fit mode is rejected with 400 rather than surfacing
|
|
// as a 500 from the image processor's default branch.
|
|
func TestHandleImageEnc_InvalidFitMode_Returns400(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
encGen, srv := newEncTestServer(t)
|
|
|
|
token, err := encGen.Generate(&encurl.Payload{
|
|
SourceHost: "cdn.example.com",
|
|
SourcePath: "/photo.jpg",
|
|
Width: 800,
|
|
Height: 600,
|
|
FitMode: imgcache.FitMode("bogus"),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Generate() error = %v", err)
|
|
}
|
|
|
|
rec := getEncToken(srv, token)
|
|
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadRequest)
|
|
}
|
|
}
|