docs(web/guides): fix audited errors in file-uploads and authorization-and-filters guides - #3096
Conversation
…n guides Guide-behavioral-audit batch 2 (p1-8-uploads, p1-9-authz) corrections: file-uploads-and-downloads.mdx: - fileField="user.avatar" -> "user[avatar]" (dotted form hard-errors on Lucee 7 and Adobe 2023; the helper renders name="user[avatar]") - store cffile.contentType & "/" & cffile.contentSubType (contentType is the major type only, so the MIME allowlist never matched) - filePath default resolves under the web root (public/files/), and directory="/abs/path" is currently broken on Adobe CF / paths containing /wheels (#3077) - range requests: Lucee serves 206 Partial Content via sendFile; Adobe returns full 200 (was claimed universally unsupported) - security: document sendFile's built-in traversal rejection (Wheels.InvalidPath) and Content-Disposition name stripping authorization-and-filters.mdx: - after-filters run in registration order, not reverse - verifies() handler example now redirects; documented the back-redirect contract that discards render-only handler output - super.config() added to all six config() examples + caution Aside (omitting it silently drops protectsFromForgery on the default template) - flattened unreachable app/controllers/Admin/Users.cfc example to AdminUsers.cfc and explained why the nested path 404s by default - clarified the binding-off failure mode (undefined-variable error + one-time wheels.log hint) and the no-handler abort outcome (blank 200) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Peter Amiri <peter@alurium.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer
TL;DR: This docs-only PR corrects audited errors in the file-uploads and authorization-and-filters guides (work items p1-8-uploads / p1-9-authz). I independently verified every source-checkable claim in the diff against the framework code and found zero discrepancies — verdict: approve.
Verification performed
Each new factual claim was checked against the framework source rather than taken on faith:
- After-filters run in registration order — confirmed:
$runFilters(vendor/wheels/controller/filters.cfc:107-131) iteratesfilterChain(type)in array order for bothbeforeandafter; no reversal exists anywhere in the chain. The old "run in reverse" text was indeed wrong. verifies()handler must redirect — confirmed:vendor/wheels/controller/verifies.cfc:147-150runs the handler then issuesredirectTo(back="true")if no redirect was performed, so the oldrenderText(..., status=400)example's response was discarded behind a 302. The referrer-or-root fallback in the new Aside matchesvendor/wheels/controller/redirection.cfc:106-122.- No handler + no redirect args → blank 200 — confirmed:
verifies.cfc:162setsvariables.$instance.abort, andvendor/wheels/controller/processing.cfc:43skips rendering without setting an error status. super.config()/ CSRF caution — confirmed: the default templateapp/controllers/Controller.cfc:24callsprotectsFromForgery()inconfig(), and the dev-mode warn-once registry exists with the exact quoted message text (vendor/wheels/Controller.cfc:153, surfaced inevents/onrequestend/debug.cfm:437-442andwheels.log).- Binding-off failure wording — confirmed:
Dispatch.cfc:752-811gates the hint onenvironment != "production", dedups one warning per controller+action per application lifetime, and writes to thewheelslog file. The guide's new wording matches exactly. fileField="user[avatar]"— confirmed the helper renders bracket names:$tagNamebuildsobjectName[property](vendor/wheels/view/miscellaneous.cfc:656-671), so the dotted form can never match the actual form-field name.sendFile()security baseline — confirmed:..traversal infile/directorythrowsWheels.InvalidPathincluding URL-decoded and backslash variants, null bytes stripped (vendor/wheels/controller/miscellaneous.cfc:238-250); the downloadnamestrips CR/LF/quotes/backslashes beforeContent-Disposition(miscellaneous.cfc:335-336,345).filePathdefault /directorybreakage — confirmed: default is"files"(vendor/wheels/events/init/views.cfm:13) resolved viaExpandPathunder the web root, and the/wheels-segment hijack code path the caution describes is visible atmiscellaneous.cfc:271-285. Issue #3077 is open and matches the citation; the deliberately-deferred #3075 is likewise open and correctly scoped out.
Runtime-only claims (cffile contentType/contentSubType split, Lucee-vs-Adobe range-request behavior, exact engine error strings) cannot be re-derived from this repo's source; they come from the PR's live Lucee 7 + Adobe 2023 verification run and are documented with appropriately engine-scoped wording, plus pnpm verify:docs reports 12/12 tagged blocks passing.
Conventions
No findings. All edited code examples use consistently named arguments (filters(through=..., except=...), flashInsert(error=...), redirectTo(route=...)), and every config() example now correctly leads with super.config().
Commits
docs(web/guides): fix audited errors in file-uploads and authorization guides — type and scope conform to commitlint.config.js; docs-type PR correctly ships no changelog fragment.
…nnot-serve-files-out Resolves the file-uploads-and-downloads.mdx conflict against ##3096's rewritten wording: the it-is-currently-broken caution is replaced with the fixed-behavior sentence while keeping develop's other improvements. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> # Conflicts: # web/sites/guides/src/content/docs/v4-0-0/digging-deeper/file-uploads-and-downloads.mdx
Guide-behavioral-audit batch 2 docs-fix wave — work items p1-8-uploads and p1-9-authz. Every change below corrects a
docs-wrongfinding from the live cross-engine verification run (Lucee 7 + Adobe 2023, source-corroborated), or applies the verifier's optional Asides from the same sketch. Behavior that is broken-but-unfixed is documented as current behavior with an issue citation.digging-deeper/file-uploads-and-downloads.mdx
fileField="user.avatar"→fileField="user[avatar]"in bothcffileexamples, plus a prose note. Evidence (p1-8-uploads-C05): the dotted form hard-errors on both engines — Lucee 7: "Form field [user.avatar] is not a file field, valid field names are [user[avatar]]"; Adobe 2023: "The form field user.avatar did not contain a file." The bracket form succeeds on both.cffile.contentType & "/" & cffile.contentSubTypein both examples (with comment). Evidence (C05):cffile.contentTypeis the major type only ("image") on Lucee AND Adobe, so the guide's ownListFindNoCase("image/jpeg,image/png,image/gif", ...)validation rejected every valid upload.filePathdefault wording: resolves under the web root (public/files/in the default layout), not "the app root" (C13). Added a caution thatdirectory="/abs/path"is currently broken on Adobe CF (web-root-prefixed →Wheels.FileNotFound) and on every engine for paths containing/wheels— cites sendFile(directory="/abs/path") cannot serve files outside the web root on Adobe CF, and hijacks any path containing "/wheels" on all engines #3077 (C14).206 Partial ContentwithContent-Range/Accept-RangesforsendFile()responses; Adobe returns the full200. Kept the servlet/nginx advice for the portable case.sendFile()rejects..traversal infile/directory(Wheels.InvalidPath, incl. URL-encoded/backslash variants, null bytes stripped) and strips CR/LF/quotes/backslashes from the downloadname.digging-deeper/authorization-and-filters.mdx
filterChain()/$runFilterspreserve registration order for both types; live log showed A→B. No other v4 guide makes the reverse claim.verifies()handler example (authz-verifies-handler-400, docs-wrong): the oldrenderText(text="Bad request", status=400)handler never reached the client —verifies.cfcissuesredirectTo(back="true")whenever the handler didn't redirect, so users got a 302 and the 400 body was discarded (verified on both engines). The handler now redirects with a flash message, and a caution Aside documents the must-redirect contract.super.config()added to all sixconfig()examples + caution Aside (authz-superconfig-warning, docs-wrong): copying any example verbatim onto the default app template silently droppedprotectsFromForgery()CSRF protection and fired the dev-mode "overrides config() without calling super.config()" warning (live-verified, warn-once semantics).app/controllers/Admin/Users.cfc→AdminUsers.cfc(authz-admin-nested-path, docs-wrong): the nested path is unreachable with the default route set (/admin/usersparses as controlleradmin, actionusers→ 404, verified). Added a sentence explaining the flat path works through the wildcard and the nested one needs an explicit route.wheels.logentry, written in any environment except production.200, not an error status.Not touched (out of scope for this PR): repo
CLAUDE.mdAnti-Pattern 8's "ActionNotAllowed → 404" claim — tracked by #3075 and to be updated whichever way that issue resolves; thesendFiledirectory resolution code fix itself is #3077.Verification
pnpm verify:docson both files: 12 tagged blocks, 12 passed, 0 failed (exit 0).🤖 Generated with Claude Code