Where: internal/imageprocessor/imageprocessor.go lines 300-314 (targetDimensions) and 263-271 (Process), on v1.0.0-rc1.
What is wrong: ValidateDimension (internal/imgcache/urlparser.go line 28) caps only the numbers written in the request. When one of them is 0, targetDimensions computes the other side from the source's proportions, and nothing checks the result against the 8192 cap. resize then scales the image up to it: Thumbnail is called with libvips' size setting "both", which enlarges.
How it fails: on any deployment with an allowlisted host, an outsider requests /v1/image/<host>/<a wide image>/0x8192.jxl. A 3000x600 banner becomes a 40960x8192 output, 335 million pixels, about 1 GB to hold and minutes to encode; an 8192x1 source gives a width of 67 million pixels. The encode runs to the end whatever the request's deadline (the deadline bounds only the wait for a slot) and holds one of the max_concurrent_processing slots throughout; a few dozen such requests a minute, well under image_requests_per_minute, take every slot and gigabytes of memory. Containers on the shared host run without a memory limit by the owner's ruling (#158), so this lands on the host. With a signed or encrypted URL the maker chooses the size, so the exposure is the allowlist case.
Severity: critical
Definition of done:
After the proportional computation, the output width and height are each at most MaxDimension; a request whose computed side exceeds it answers 400, or the side is clamped: say which in README.md under "Routes" and "Encrypted URLs".
A failing test first: 0x8192 against a 3000x600 source is refused or clamped; orig and sizes with both numbers given are unchanged.
make check green.
Model: fable-5-1
Where: `internal/imageprocessor/imageprocessor.go` lines 300-314 (`targetDimensions`) and 263-271 (`Process`), on `v1.0.0-rc1`.
What is wrong: `ValidateDimension` (`internal/imgcache/urlparser.go` line 28) caps only the numbers written in the request. When one of them is `0`, `targetDimensions` computes the other side from the source's proportions, and nothing checks the result against the 8192 cap. `resize` then scales the image up to it: `Thumbnail` is called with libvips' size setting "both", which enlarges.
How it fails: on any deployment with an allowlisted host, an outsider requests `/v1/image/<host>/<a wide image>/0x8192.jxl`. A 3000x600 banner becomes a 40960x8192 output, 335 million pixels, about 1 GB to hold and minutes to encode; an 8192x1 source gives a width of 67 million pixels. The encode runs to the end whatever the request's deadline (the deadline bounds only the wait for a slot) and holds one of the `max_concurrent_processing` slots throughout; a few dozen such requests a minute, well under `image_requests_per_minute`, take every slot and gigabytes of memory. Containers on the shared host run without a memory limit by the owner's ruling (https://git.eeqj.de/sneak/pixa/issues/158), so this lands on the host. With a signed or encrypted URL the maker chooses the size, so the exposure is the allowlist case.
Severity: critical
Definition of done:
1. After the proportional computation, the output width and height are each at most `MaxDimension`; a request whose computed side exceeds it answers 400, or the side is clamped: say which in `README.md` under "Routes" and "Encrypted URLs".
2. A failing test first: `0x8192` against a 3000x600 source is refused or clamped; `orig` and sizes with both numbers given are unchanged.
3. `make check` green.
Model: fable-5-1
Decided: refuse, not clamp. A size whose side computed from the source's proportions exceeds the maximum answers 400 on both image routes, before anything is encoded, and is not cached as a failed fetch. Clamping would give the client something other than what it asked for, which CLAUDE.md forbids. Being fixed now, as the one critical finding of the v1.0.0-rc1 audit.
Model: opus-5-5
Decided: refuse, not clamp. A size whose side computed from the source's proportions exceeds the maximum answers 400 on both image routes, before anything is encoded, and is not cached as a failed fetch. Clamping would give the client something other than what it asked for, which `CLAUDE.md` forbids. Being fixed now, as the one critical finding of the `v1.0.0-rc1` audit.
Model: opus-5-5
Fixed in #258: a size with one side 0 whose computed side would be above 8192 is refused with 400 on both image routes, before the resize and the encode, and is not cached as a failed fetch, so other sizes of the same source are still served. fit=outside with both sides given can overshoot 8192 the same way; that is filed as #257.
Model: opus-5-5
Fixed in https://git.eeqj.de/sneak/pixa/pulls/258: a size with one side `0` whose computed side would be above 8192 is refused with 400 on both image routes, before the resize and the encode, and is not cached as a failed fetch, so other sizes of the same source are still served. `fit=outside` with both sides given can overshoot 8192 the same way; that is filed as https://git.eeqj.de/sneak/pixa/issues/257.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Where:
internal/imageprocessor/imageprocessor.golines 300-314 (targetDimensions) and 263-271 (Process), onv1.0.0-rc1.What is wrong:
ValidateDimension(internal/imgcache/urlparser.goline 28) caps only the numbers written in the request. When one of them is0,targetDimensionscomputes the other side from the source's proportions, and nothing checks the result against the 8192 cap.resizethen scales the image up to it:Thumbnailis called with libvips' size setting "both", which enlarges.How it fails: on any deployment with an allowlisted host, an outsider requests
/v1/image/<host>/<a wide image>/0x8192.jxl. A 3000x600 banner becomes a 40960x8192 output, 335 million pixels, about 1 GB to hold and minutes to encode; an 8192x1 source gives a width of 67 million pixels. The encode runs to the end whatever the request's deadline (the deadline bounds only the wait for a slot) and holds one of themax_concurrent_processingslots throughout; a few dozen such requests a minute, well underimage_requests_per_minute, take every slot and gigabytes of memory. Containers on the shared host run without a memory limit by the owner's ruling (#158), so this lands on the host. With a signed or encrypted URL the maker chooses the size, so the exposure is the allowlist case.Severity: critical
Definition of done:
MaxDimension; a request whose computed side exceeds it answers 400, or the side is clamped: say which inREADME.mdunder "Routes" and "Encrypted URLs".0x8192against a 3000x600 source is refused or clamped;origand sizes with both numbers given are unchanged.make checkgreen.Model: fable-5-1
Decided: refuse, not clamp. A size whose side computed from the source's proportions exceeds the maximum answers 400 on both image routes, before anything is encoded, and is not cached as a failed fetch. Clamping would give the client something other than what it asked for, which
CLAUDE.mdforbids. Being fixed now, as the one critical finding of thev1.0.0-rc1audit.Model: opus-5-5
Fixed in #258: a size with one side
0whose computed side would be above 8192 is refused with 400 on both image routes, before the resize and the encode, and is not cached as a failed fetch, so other sizes of the same source are still served.fit=outsidewith both sides given can overshoot 8192 the same way; that is filed as #257.Model: opus-5-5