In a Livewire component, why validate the TemporaryUploadedFile yourself, and which parts of it can the user still control?
answer
- global rules are only a floor
- validate before store()
- MIME type sniffed from contents
- client name is user input
- endpoint errors land on the property
basics
~20 sLivewire's upload endpoint only enforces the global rules, so the component must validate the temporary file against the field's own rules before storing it; the original file name the user sent must still be treated as untrusted input.
solid answer
~40 sA file reaching `$this->receipt` has passed only the **global** `temporary_file_upload.rules` (by default `required|file|max:12288`), which apply to every upload in the app. The receipt field's own rules, such as PDF or image only and 5 MB at most, must be checked in the component with Livewire's validation before `store()`. `TemporaryUploadedFile` extends Laravel's `UploadedFile`, so file rules work on it; its `getMimeType()` is detected from the file's contents rather than from what the browser claimed. Two values stay user-controlled: `getClientOriginalName()`, which comes from the upload's metadata, and the file's contents themselves. So store under a generated name, keep the original name only for display, and never build a path from it. Errors from the global rules are reported on the bound property, so the same error display covers both layers.
go deeper
Recall that you validate the uploaded property in the component before calling store().
Explain the global endpoint rules versus field rules, content-based MIME detection and why the original name is untrusted.
Account for S3 direct uploads skipping the endpoint rules, and validate multi-file arrays and sizes explicitly.
Define an upload policy per file category, from size limits to scanning, that every component follows.
## Two validation layers A Livewire upload passes through two separate checks, and it is easy to assume the first one is enough. 1. **The upload endpoint.** When the file is posted to Livewire's signed temporary URL, the controller validates it against `config('livewire.temporary_file_upload.rules')`. With nothing configured the fallback is `required`, `file` and `max:12288`. These rules are **global**: they apply to avatars, receipts and CSV imports alike, so they can only be a loose floor. 2. **Your component.** After the file is stored temporarily and the property is set, nothing else is checked unless you validate it. The rules for this field, such as "a PDF, JPEG or PNG receipt of at most 5 MB", belong here, and they must run **before** `store()` copies the file to permanent storage. If the endpoint rejects a file, Livewire reports the error on the bound property name, so the same `@error('receipt')` display shows both kinds of failure. ## What Livewire makes trustworthy - **Which temporary file the property points at.** The reference sent back to the component is a path signed with an HMAC derived from the app key, and Livewire aborts with 403 if the signature does not match, so a user cannot swap in another temporary file by editing the request. - **The MIME type.** On a real upload, `getMimeType()` is detected from the first bytes of the stored file, not from the browser's `Content-Type`, so a renamed executable does not become `application/pdf` just by claiming it. ## What the user still controls | Value | Source | Safe use | |---|---|---| | file contents | the user | validate type and size; scan if the risk warrants | | `getClientOriginalName()` | metadata sent with the upload | display only, escaped | | extension in the original name | the user | do not trust; rely on content detection | | number of files on a `multiple` input | the user | validate the array size | Building a storage path from `getClientOriginalName()` invites collisions, odd characters and path tricks; `store()` with its generated hash name, or `storeAs()` with a name you construct from the claim's ID, avoids that. ## Validating in practice - Validate in the save action before storing, and optionally when the property updates, so the user sees an error as soon as the file is chosen instead of after submitting. - For `multiple` inputs, validate each element (`receipts.*`) and the array's size. - Keep the component's rules stricter than the global ones; the global set exists to stop abuse of the endpoint, not to express business rules. - When the temporary disk is S3, files upload straight to the bucket and **skip** the endpoint entirely, so the global rules never run and the component's validation is the only check. ## Common interview traps 1. "Livewire already validated it" is the wrong answer: only the global, app-wide rules ran. 2. Trusting the browser's extension or `Content-Type`, when the stored file's detected MIME type is what the file rules should rely on. 3. Using the original file name as the storage key. 4. Storing first and validating afterwards, leaving invalid files in permanent storage. ## A good answer in one breath "Livewire's endpoint only applies the app-wide floor, so I validate each field in the component before storing, rely on the detected MIME type rather than the browser's, store under a generated name and keep the original name for display. If the temporary disk is S3, the floor does not even run, so the component rules are everything."
- Why does moving the temporary disk to S3 make component-level validation even more important?With an S3 temporary disk, the browser uploads directly to the bucket with a presigned URL, so Livewire's upload endpoint and its global rules never run. The file reaches the component unchecked, and the component's validation is the only size and type check the file gets.
- How would you show an invalid-file error as soon as the receipt is chosen rather than on submit?Validate the property when it changes, for example in an `updatedReceipt()` hook that validates just that field. The upload completing sets the property through Livewire's update path, so the hook fires and the error appears immediately.
saying these in an interview costs you the question
- Says Livewire's upload endpoint already enforces each field's own rules.
- Trusts the browser-supplied extension to decide the file type.
- Builds the storage path from getClientOriginalName().
- Validates after calling store() instead of before.
- Believes a user can point the property at any file on the temporary disk.