A picture is checked by its pixels before it is opened, and a map upload is checked in the worker (#1074) #1078
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/1074-image-pixel-bounds"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #1074.
Summary
validate_and_reencodetakes a keyword-onlymax_pixelswith no default and checks the header first. A test asserts every call inapp/passes it.4080 × 3072(12.53 million pixels). It takes the common 4032×3024 phone photo. Uploads and imports share it; it replaces the 4000×3000 that #1076 shipped for imports.8192 × 6144.Behaviour that changes for a person uploading
6 × frame + total / 4 ≤ bound, at most 1,000 framesAnimated GIF examples: 480×270 up to 362 frames, 800×600 up to 80, 1280×720 up to 30. The largest frame is 1.93 MP.
Measured
Validator alone,
docker run -m 512m, peak over the state before the call:Through nginx on a warm backend, own compose project:
oom_killstayed 0 throughout.Known limits
Changelog
changelog.d/1074-image-pixel-bounds.security.mdTest plan
7348 passed, 14 skipped, 107 deselected.tests/export,tests/wiki, the damaged-image, session-hygiene, enqueue and nginx tests:1123 passed. Lint clean. Version and changelog checks OK.Map.test.jsxandMapCanvas.test.jsx,17 passed; eslint 0 errors.unclassified: none.🤖 Generated with Claude Code
Not to be merged yet: two things.
1. Independent review of
aef9ee26: one confirmed bypass and seven smaller defects. Fixes are in progress on this branch.uploadedfile behind.Found sound
Not checked by the review: metadata stripping on re-encode, whether a pending file can be served, and WebP edge cases. The builder is answering the first two by running.
Pre-existing, to be filed separately: EXIF orientation is never applied, so a photo that relies on the tag is stored sideways.
2. This run's CI failure is the runner's disk, not the code
All four failed jobs stopped on
no space left on device:500 passed, on aDiskFullErrorfrom Postgres;The next push will start a fresh run.
All eight review findings fixed in
6d03120b, each with a test. The CI runner's disk was full for the last run, so the evidence below is from the dev host./api/mewhile a decode blocksoriginal.<hash>.<ext>and switched by the row; a redelivery after success is read as doneAPNG and animated WebP do not have hole 1: Pillow refuses an APNG frame past the canvas, and libwebp refuses a forged oversized frame.
What changes for a person, beyond the table in the description
Two answers the review asked for
uploaded,imported,.partialand their.checkingforms) cannot be served. They sit underprivate/, nginx answers 404 there, and the backend redirects only to paths held in a row.Ran at
6d03120b7387 passed, 14 skipped, 107 deselected.oom_kill 0and no restart throughout.Not run: journeys, the bot and synthetic suites, and the frontend (untouched since the first commit). The progressive figures in finding 2 are from the validator alone.
One more fix,
3fc0c32a: the progressive-JPEG weight was too narrow.A second, narrow review of the two raw parsers was started and did not complete, so I read them myself.
The GIF walk: I found no disagreement with Pillow's reader, by reading both.
This was not tested with crafted files beyond the ones the builder and the first reviewer ran.
The JPEG profile: one gap, fixed.
SOF2frame alone. libjpeg holds the whole picture's coefficients for more than that:SOF2,SOF6, and the arithmetic-codedSOF10andSOF14);jdinput.c:comps_in_scan < num_componentssetshas_multiple_scans).Ran at
3fc0c32atests/wiki/test_image_pixel_bounds.py:77 passed, with ten new cases for the frame types and scan shapes.Not re-run: the full backend suite (CI) and the end-to-end pass.