fix(controller): harden $callAction ViewNotFound and $useLayout wipe (B5–B6) - #3401
Merged
Merged
Conversation
Adversarial specs for $callAction ViewNotFound masking, $useLayout
later-match wipe, redirect-only action cache, live filterChain("all"),
processAction halt return, $findRoute multi-name fail-open, processRequest
CSRF ignore, prepend through-order, redirectTo(url=) encoding, and blank
layout-function returns.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Peter Amiri <peter@alurium.com>
Map ViewNotFound only for genuine missing-view includes, stop later non-matching usesLayout from wiping a prior match, skip caching redirect-only action bodies, copy filterChain(), return processAction halt, fail closed on unmatched same-named $findRoute, keep prepend through-order, encode redirectTo(url=) params, and treat a blank layout-function return as the default layout. processRequest CSRF ignore stays the default; csrf=exception is opt-in. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com>
Clear dummy class layouts between specified_layouts examples so they do not depend on the $useLayout wipe. Accept EncodeForURL space as either %20 or +. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com>
$throwErrorOrShow404Page aborts without a typed exception when showErrorInformation is off, so leftover CI suite state left thrown.type empty on ViewNotFound and HardenerLayoutError. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com>
Genuine missing views now Throw Wheels.ViewNotFound instead of $throwErrorOrShow404Page include+abort. processAction still presents the production 404 page so HTTP 404 for apps is unchanged. B5 specs pin params.format=html (leftover Accept skipped auto-render) and set showErrorInformation=false so the typed throw is proven. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Controller / request-lifecycle hardener slice on top of #3400 (B1–B4, B7–B8 already on develop). Draft for Lead/CoS review.
BLOCKERs
$callActionauto-render catch mapped any exception toWheels.ViewNotFoundwhenaction.cfmwas missing, masking layout/render errors. Now only genuine missing-view includes ($isMissingViewException+ missing file) become typedWheels.ViewNotFound; other types are rethrown.$useLayoutresetrv = useDefaulton every loop pass, so a later non-matchingusesLayoutwiped a prior match (silent layout bypass). Once a match is chosen, later non-matches leave it alone; a later match still overrides.SHOULDs touched
$callActionAndAddToCache— does not store a cache entry for a redirect-only action (empty body / blank 200 on hit).filterChain("all")— returns aDuplicate()copy so callers cannot mutate the live$class.filtersarray.processActionhalt — returnsfalsewhen a verification aborts or a before filter returnsfalse. Additive: success path is stilltrue. Callers that ignore the return are unchanged.$findRoutemulti-name — no longer fail-opens to the last same-named candidate. Selects the first candidate matching method (when supplied) and required variables; throwsWheels.RouteNotFoundwhen none match.processRequestCSRF ignore — default staysignore(historic test helper). Opt-incsrf="exception"/csrf="abort"enforces. ProductionprotectsFromForgery()default is unchanged.filters(placement="prepend")+ multi-through—a,b,cstaysa,b,cin front of the existing chain (was reversed toc,b,a).redirectTo(url=)params — encoded via$constructParamslikeback=true.useDefault(matches the in-source contract / docs), not as “no layout”.renderingSpecspecified_layoutsexamples now clear dummy class layouts so they test one declaration at a time and no longer depend on the B6 wipe.Lead accepted (a) on the three public flips (
$findRouteRouteNotFound,redirectTo(url=)encode, blank layout-fn → default). Those stay as shipped.B5 — why
974b3645ewas UNPROVEN on LuCLI, and what2f440a27echangesLuCLI 32721709696: both B5 its did not throw. The
showErrorInformation=truepin was the wrong lever.Root cause (full-suite only):
$callActionskips auto-render when$requestContentType()is nothtmland the format is not in$acceptableFormats. Earlier bundles (providesSpec) writerequest.cgi.http_accepttoapplication/json/application/pdfon the sharedrequest.cgistruct ($$oldCGIScope = request.cgiis a reference, not a copy). After those specs, Accept is leftover non-html, auto-render never runs,$useLayout/ exploding layout never run, both its finish cleanly. Isolatedfilter=hardenernever loadsprovidesSpec, so it stayed green.$throwErrorOrShow404Pageabort was a second swallow if the missing-view path was reached withshowErrorInformationoff.Production change (flagged — not a public HTTP 404 flip):
$callActionnow always Throws typedWheels.ViewNotFoundfor a genuine missing view ($header(404)thenThrow). It no longer include+aborts via$throwErrorOrShow404Page.processAction(HTTP dispatch) catchesWheels.ViewNotFoundand still calls$throwErrorOrShow404Page, so apps still get HTTP 404 +onmissingtemplatewhenshowErrorInformationis off.Throw(object = e)with their original type.Test change: B5 pins
params.format=html+request.cgi.http_accept=text/html, and setsshowErrorInformation=falseviaset()so the typed Throw is proven — not the old throw-path pin.PROVEN / UNPROVEN
wheels test --core --ci --filter=hardener→ 41 passed (showErrorInformation=false)$useLayoutwipefilterChain("all")copyprocessActionhalt return$findRoutemulti-name fail-openprocessRequestCSRF default flipignore. Do not merge a silent flip.processRequestCSRF opt-incsrf="exception"redirectTo(url=)encode%20or+) — Lead accepted (a)super.config()CSRF$mergeUrlAndFormScopesX-CSRF-Tokenheader splitverifieshandler fallthrough$runVerificationsalreadyredirectTo(back=true)$currentUserForPolicycatch → guestonlyProvidesunacceptable format → HTMLonly+exceptOR semanticsDefault-flip flags (Lead / CoS)
processRequestCSRFwith="ignore"default: DO NOT MERGE a silent flip. Opt-incsrf="exception"only. ProductionprotectsFromForgery()remainsexception.processActionreturningfalseon halt is additive (was alwaystrue).$findRoutethrowingRouteNotFoundinstead of returning the last same-named candidate is fail-closed. Lead accepted (a).$callActionViewNotFound is now always a typed Throw. HTTP 404 viaprocessActionis unchanged. Direct$callActioncallers (tests / custom dispatch) no longer see include+abort whenshowErrorInformationis off.HEAD
2f440a27e1f96069c527d67f4637177162d567b6Test filters
Green counts (this HEAD)
hardenershowErrorInformation=false)controllerRelated
Follow-on to #3400. Does not re-do B1–B4 / B7–B8.
Type of Change
Feature Completeness Checklist
processRequest(csrf=)is test-helper opt-inchangelog.d/controller-hardener-b5-b6.fixed.md,changelog.d/controller-hardener-shoulds.changed.mdhardener+controlleron this HEAD