Skip to content

docs(web/guides): fix audited errors in file-uploads and authorization-and-filters guides - #3096

Merged
bpamiri merged 1 commit into
developfrom
peter/docs-audit2-uploads-authz
Jun 12, 2026
Merged

docs(web/guides): fix audited errors in file-uploads and authorization-and-filters guides#3096
bpamiri merged 1 commit into
developfrom
peter/docs-audit2-uploads-authz

Conversation

@bpamiri

@bpamiri bpamiri commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Guide-behavioral-audit batch 2 docs-fix wave — work items p1-8-uploads and p1-9-authz. Every change below corrects a docs-wrong finding 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 both cffile examples, 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.contentSubType in both examples (with comment). Evidence (C05): cffile.contentType is the major type only ("image") on Lucee AND Adobe, so the guide's own ListFindNoCase("image/jpeg,image/png,image/gif", ...) validation rejected every valid upload.
  • filePath default wording: resolves under the web root (public/files/ in the default layout), not "the app root" (C13). Added a caution that directory="/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).
  • Range requests (C20, docs-wrong): replaced the universal "cfcontent doesn't emit them" with engine-scoped wording — Lucee 7 on the stock dev stack DOES return 206 Partial Content with Content-Range/Accept-Ranges for sendFile() responses; Adobe returns the full 200. Kept the servlet/nginx advice for the portable case.
  • Security section (C25 sketch): documented the framework baseline — sendFile() rejects .. traversal in file/directory (Wheels.InvalidPath, incl. URL-encoded/backslash variants, null bytes stripped) and strips CR/LF/quotes/backslashes from the download name.

digging-deeper/authorization-and-filters.mdx

  • "After-filters run in reverse" → run in registration order (authz-after-filter-reverse, docs-wrong): filterChain()/$runFilters preserve 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 old renderText(text="Bad request", status=400) handler never reached the client — verifies.cfc issues redirectTo(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 six config() examples + caution Aside (authz-superconfig-warning, docs-wrong): copying any example verbatim onto the default app template silently dropped protectsFromForgery() CSRF protection and fired the dev-mode "overrides config() without calling super.config()" warning (live-verified, warn-once semantics).
  • Flattened app/controllers/Admin/Users.cfcAdminUsers.cfc (authz-admin-nested-path, docs-wrong): the nested path is unreachable with the default route set (/admin/users parses as controller admin, action users → 404, verified). Added a sentence explaining the flat path works through the wildcard and the nested one needs an explicit route.
  • Binding-off failure mode clarified (sketch item 5): the on-screen result is an undefined-variable error; the enable-binding hint is a one-time-per-controller+action wheels.log entry, written in any environment except production.
  • No-handler abort outcome (sketch item 6): documented that a failed verification with no handler and no redirect args aborts to a blank 200, not an error status.

Not touched (out of scope for this PR): repo CLAUDE.md Anti-Pattern 8's "ActionNotAllowed → 404" claim — tracked by #3075 and to be updated whichever way that issue resolves; the sendFile directory resolution code fix itself is #3077.

Verification

pnpm verify:docs on both files: 12 tagged blocks, 12 passed, 0 failed (exit 0).

🤖 Generated with Claude Code

…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>

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) iterates filterChain(type) in array order for both before and after; 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-150 runs the handler then issues redirectTo(back="true") if no redirect was performed, so the old renderText(..., status=400) example's response was discarded behind a 302. The referrer-or-root fallback in the new Aside matches vendor/wheels/controller/redirection.cfc:106-122.
  • No handler + no redirect args → blank 200 — confirmed: verifies.cfc:162 sets variables.$instance.abort, and vendor/wheels/controller/processing.cfc:43 skips rendering without setting an error status.
  • super.config() / CSRF caution — confirmed: the default template app/controllers/Controller.cfc:24 calls protectsFromForgery() in config(), and the dev-mode warn-once registry exists with the exact quoted message text (vendor/wheels/Controller.cfc:153, surfaced in events/onrequestend/debug.cfm:437-442 and wheels.log).
  • Binding-off failure wording — confirmed: Dispatch.cfc:752-811 gates the hint on environment != "production", dedups one warning per controller+action per application lifetime, and writes to the wheels log file. The guide's new wording matches exactly.
  • fileField="user[avatar]" — confirmed the helper renders bracket names: $tagName builds objectName[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 in file/directory throws Wheels.InvalidPath including URL-decoded and backslash variants, null bytes stripped (vendor/wheels/controller/miscellaneous.cfc:238-250); the download name strips CR/LF/quotes/backslashes before Content-Disposition (miscellaneous.cfc:335-336,345).
  • filePath default / directory breakage — confirmed: default is "files" (vendor/wheels/events/init/views.cfm:13) resolved via ExpandPath under the web root, and the /wheels-segment hijack code path the caution describes is visible at miscellaneous.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.

@bpamiri
bpamiri merged commit 79cd0d3 into develop Jun 12, 2026
14 checks passed
@bpamiri
bpamiri deleted the peter/docs-audit2-uploads-authz branch June 12, 2026 11:34
bpamiri added a commit that referenced this pull request Jun 12, 2026
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant