Files
sim/apps/sim/types
Waleed ff1ea218dc fix(uploads): bound the HEIF fallback decode by declared pixels, not just bytes (#6456)
* fix(uploads): bound the HEIF fallback decode by declared pixels, not just bytes

The WebAssembly fallback allocates width * height * 4 up front, taking the size
straight from the container and building the buffer before the codec is asked for
anything — so a file that never decodes still costs the memory. Only a 20MB byte
ceiling stood in front of it, and bytes do not bound a declared raster: a small
container can name dimensions up to libheif's own default of ~1.07e9 pixels, about
4.3GB as RGBA.

Read the declared dimensions with `heic-decode`'s `all()`, which parses the
container and reports each image's size while leaving the decode for `decode()`,
and refuse above 100MP. That caps the allocation near 400MB and clears every phone
camera — a 48MP iPhone still is 8064x6048.

`heic-decode` was already present as a transitive dependency of `heic-convert`;
this promotes it to a direct one at the same version, since the code now imports it.
Local types cover only the surface used, and a test pins that `all` really is a
named ESM export — a CJS `module.exports = one` need not surface it, and if it
stopped, the check would throw, get swallowed by the catch, and quietly stop
guarding with mocked tests still green.

* fix(uploads): free the HEIF decoder handles after the pixel check

all() hands back live libheif handles and leaves freeing them to the caller —
the default export does it in a finally, this path did not. Every HEIF preview
would have leaked the decoder context on the WebAssembly heap.

Copy the two numbers out, dispose in a finally so a throw mid-read still frees,
and declare dispose as required on the returned array so a caller cannot omit it.

* test(uploads): pin that all() reports dimensions before decoding

The pixel guard is only worth anything if the declared size is available up
front. Were dimensions to move behind decode() — as they already are on the
default export — width * height would silently become NaN and the check would
stop rejecting anything, with every mocked test still green.

Drives the real mapping with a stub libheif, so the contract is exercised
without needing a HEVC encoder to build a valid container.
2026-08-08 15:45:29 -07:00
..