Repository navigation
Limit the public image, upload and answer endpoints - #212
Conversation
- Serve only form images, in the variants the app requests, and throttle the image route. - Check uploads against the file question's allowed types, size and count, require a file, and throttle the upload route. - Cap answer and session parameter size. - A submit that announces uploads replaces the earlier files of those questions, so a retry fits the count. - The editor's max file size matches the server's 8 MB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PhilReinking
left a comment
There was a problem hiding this comment.
Changes at 55272bc, CI green. Two musts: a retry after a failed upload deletes files that went through, and the changelog line.
Checked beyond the PR text, on a local server and the pr-212 image: logo, background and editor preview load, also on a duplicated form; other image variants 404 and store nothing; refused type, missing answer, no file and a 4th file get 422; the editor saves 8 for 20; 7.9 MB uploads, 8.0 MB gets nginx's 413.
|
|
||
| // the form page uploads all files again after this call, so a retry must not count the earlier ones | ||
| if ($request->boolean('is_uploading')) { | ||
| // strings, so MySQL can't match a numeric key against every uuid | ||
| $session->deleteUploads(array_map('strval', array_keys($request->input('payload')))); | ||
| } |
There was a problem hiding this comment.
On a retry the form page skips files that already went through (91f921e), so this deletes them for good. Tried it: small.pdf uploaded, big.pdf refused, retry: the answer lists small.pdf, no file left. Drop the cleanup and deleteUploads, or make the form re-send every file on a retry.
| // the form page uploads all files again after this call, so a retry must not count the earlier ones | |
| if ($request->boolean('is_uploading')) { | |
| // strings, so MySQL can't match a numeric key against every uuid | |
| $session->deleteUploads(array_map('strval', array_keys($request->input('payload')))); | |
| } |
| - **Upgrade step:** form links must start with http(s):// or mailto:. Saved links that don't (like a bare social handle or a www. address) are hidden on the public form: enter them again as full URLs. Templates with such links don't import. Form texts are cleaned before they show. (#193) | ||
| - Webhook responses show as plain text in the submissions view. (#196) | ||
| - Question labels in the logic editor's pickers show as plain text. (#199) | ||
| - Public forms have limits: images load only in the sizes the form uses, uploads must match the question's file types, size and count (at most 8 MB per file, also in the editor), answers can be up to 30 KB and form link parameters up to 8 KB. Images and uploads are rate-limited per visitor, so behind a proxy check that `TRUSTED_PROXIES` lists it. (#212) |
There was a problem hiding this comment.
Say plainly that the image address now only serves form logos and backgrounds:
| - Public forms have limits: images load only in the sizes the form uses, uploads must match the question's file types, size and count (at most 8 MB per file, also in the editor), answers can be up to 30 KB and form link parameters up to 8 KB. Images and uploads are rate-limited per visitor, so behind a proxy check that `TRUSTED_PROXIES` lists it. (#212) | |
| - Public forms have limits: the image address only serves form logos and backgrounds, in the sizes the form uses. Uploads must match the question's file types, size and count (at most 8 MB per file, also in the editor), answers can be up to 30 KB and form link parameters up to 8 KB. Images and uploads are rate-limited per visitor, so behind a proxy check that `TRUSTED_PROXIES` lists it. (#212) |
| { | ||
| $request->validate([ | ||
| // params come from the form link, and nginx stops URLs at 8 KB | ||
| 'params' => ['array', 'nullable', new MaxSize(8192)], |
There was a problem hiding this comment.
Serialized, each param adds about 15 bytes, so a 4 KB link with 500 short params is refused. The form page then doesn't load at all, there's no retry message. Params are stored as JSON, so measure the JSON length here: that's close to the link's length.
The form page skips files that already went through on a retry, so the cleanup deleted them for good. The upload count check still holds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Serialized, each param adds about 15 bytes, so a long form link with many short params was refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nginx and post_max_size refused the request of an 8 MB file, since the request is a bit larger than its file. Now PHP's upload limit and the upload checks decide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PhilReinking
left a comment
There was a problem hiding this comment.
OK at 1b8792d, CI green. All points from the last review are fixed.
Checked beyond the PR text, on a local server: small.pdf uploads, big.pdf is refused, the retry and the submit without big.pdf both keep small.pdf, and the form ends on "Form Submitted". On the pr-212 image, 8.0 MB uploads and 8.1 MB gets a 422. A link with 500 short params now opens a session, and one 8.3 KB param gets a 422.
…blic-endpoints # Conflicts: # routes/web.php
The public image, upload and answer endpoints accepted any image parameters, any upload and answers of any size. This limits them to what the app uses and to each question's settings.
Fixes BITS-156
post_max_sizeallow 9 MB, so an 8 MB file reaches PHP and its upload checks.Heads-up: an upload outside the question's settings, an answer over 30 KB or an overlong form link now fails with 422, and respondents see the retry message. Questions saved with more than 8 MB keep the value, but uploads stop at 8 MB. Behind a proxy,
TRUSTED_PROXIESmust list it, or all visitors share one limit.Tested: full PHP suite. Each new limit test fails without its fix. Both configs load in the base image.
Not tested: by hand in a browser, a real 8 MB upload through nginx.
Details for review
w=256&q=75(editor preview),w=1600|1920|2880&fm=webp(form background). Onlyform.blade.php,ImageUpload.vueand the plain logo URL build image URLs. No mail, embed script, meta tag or doc does. Anything else is a 404 and renders nothing.🤖 Generated with Claude Code