P1: /v1/e/ encrypted URLs bypass dimension and fit-mode validation #62

Open
opened 2026-08-09 03:43:23 +02:00 by clawbot · 0 comments
Collaborator

Verified against main at 61f42e6.

HandleImageEnc (internal/handlers/imageenc.go:49-50) builds the image request straight from the decrypted payload:

req := payload.ToImageRequest()

Unlike the /v1/image/ path, nothing between decryption and processing applies:

  • the MaxDimension bound enforced by internal/imgcache/urlparser.go:24,224-226
  • ValidateFitMode

The token generator is equally lax: HandleGenerateURL (internal/handlers/auth.go:106-108) runs strconv.Atoi on width, height, quality, and ttl and discards every error, so non-numeric input silently becomes 0, and no upper bound is applied to width/height.

Consequences:

  • A generated token can request e.g. 100000x100000, which reaches libvips and blows up memory — the same class of resource exhaustion the /v1/image/ path already guards against.
  • An invalid fit mode reaches imageprocessor.resize's default branch (internal/imageprocessor/imageprocessor.go:326-328) and surfaces as a 500 rather than a 400.

This requires the signing key to exploit (the payload is sealed), so it is self-inflicted rather than remotely exploitable — but "the operator can trivially DoS their own daemon through the supplied web UI, and gets a 500 for a validation error" is not 1.0 behavior, and the asymmetry with /v1/image/ is exactly the kind of thing that rots.

Related but filed separately: repo policy forbids silently defaulting an invalid explicit value. strconv.Atoi errors becoming 0 is that defect in the handler layer.

Definition of done

  1. /v1/e/ applies the same dimension bound and fit-mode validation as /v1/image/, sharing the validation code rather than duplicating it.
  2. HandleGenerateURL checks every strconv.Atoi error and rejects bad input with a 400 naming the offending field, instead of coercing to 0. Width/height are bounds-checked at generation time too, so an unusable token cannot be minted.
  3. An invalid fit mode produces a 400, not a 500, on both routes.
  4. Failing tests first: an encrypted token carrying an over-limit dimension is rejected; one carrying an invalid fit mode returns 400; POST /generate with non-numeric width returns 400 rather than generating a 0-width token.
  5. make check green.
Verified against `main` at `61f42e6`. `HandleImageEnc` (`internal/handlers/imageenc.go:49-50`) builds the image request straight from the decrypted payload: ```go req := payload.ToImageRequest() ``` Unlike the `/v1/image/` path, nothing between decryption and processing applies: - the `MaxDimension` bound enforced by `internal/imgcache/urlparser.go:24,224-226` - `ValidateFitMode` The token generator is equally lax: `HandleGenerateURL` (`internal/handlers/auth.go:106-108`) runs `strconv.Atoi` on `width`, `height`, `quality`, and `ttl` and **discards every error**, so non-numeric input silently becomes `0`, and no upper bound is applied to width/height. Consequences: - A generated token can request e.g. 100000x100000, which reaches libvips and blows up memory — the same class of resource exhaustion the `/v1/image/` path already guards against. - An invalid fit mode reaches `imageprocessor.resize`'s default branch (`internal/imageprocessor/imageprocessor.go:326-328`) and surfaces as a 500 rather than a 400. This requires the signing key to exploit (the payload is sealed), so it is self-inflicted rather than remotely exploitable — but "the operator can trivially DoS their own daemon through the supplied web UI, and gets a 500 for a validation error" is not 1.0 behavior, and the asymmetry with `/v1/image/` is exactly the kind of thing that rots. Related but filed separately: repo policy forbids silently defaulting an invalid explicit value. `strconv.Atoi` errors becoming `0` is that defect in the handler layer. ## Definition of done 1. `/v1/e/` applies the same dimension bound and fit-mode validation as `/v1/image/`, sharing the validation code rather than duplicating it. 2. `HandleGenerateURL` checks every `strconv.Atoi` error and rejects bad input with a 400 naming the offending field, instead of coercing to `0`. Width/height are bounds-checked at generation time too, so an unusable token cannot be minted. 3. An invalid fit mode produces a 400, not a 500, on both routes. 4. Failing tests first: an encrypted token carrying an over-limit dimension is rejected; one carrying an invalid fit mode returns 400; `POST /generate` with non-numeric width returns 400 rather than generating a `0`-width token. 5. `make check` green.
clawbot added this to the 1.0.0 milestone 2026-08-09 03:43:23 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#62