Problem
CSRF protection is opt-in and silently disabled by the documented config() override footgun. vendor/wheels/controller/csrf.cfc:18-20, 37-51 no-ops unless StructKeyExists(variables.$class, "csrf"); CSRF is wired only via the generated base controller (cli/lucli/templates/app/app/controllers/Controller.cfc:23-25), which itself doesn't call super.config(). Any child controller overriding config() without super.config() silently disables CSRF for that controller, with no error or startup warning. Verified against origin/develop @ 8971094.
This is a recurring footgun (tracked around #2845; see also project memory on the super.config() chain fragility). Prior remediation chose docs + generator templates that call super.config().
Impact
A developer who overrides config() in a controller — a common, documented pattern — without calling super.config() silently ships that controller with CSRF disabled and no warning. The protection's presence is invisible and easy to drop.
Suggested approach
This is a deliberate behavior change for every existing app, so it needs an upgrade-path design rather than an autonomous flip:
- Either enforce CSRF at the dispatch/middleware layer (independent of per-controller
config()), or
- Detect-and-warn at startup when a controller overrides
config() without calling super.config().
- If making CSRF default-on, design a deprecation window / upgrade note so existing apps are not broken silently.
Acceptance criteria
Source
Internal multi-agent framework review 2026-06-09, wave 2 (issues phase). Findings: security-pass SEC-10 (S5), followups SEC-10.
Problem
CSRF protection is opt-in and silently disabled by the documented
config()override footgun.vendor/wheels/controller/csrf.cfc:18-20, 37-51no-ops unlessStructKeyExists(variables.$class, "csrf"); CSRF is wired only via the generated base controller (cli/lucli/templates/app/app/controllers/Controller.cfc:23-25), which itself doesn't callsuper.config(). Any child controller overridingconfig()withoutsuper.config()silently disables CSRF for that controller, with no error or startup warning. Verified againstorigin/develop@8971094.This is a recurring footgun (tracked around #2845; see also project memory on the
super.config()chain fragility). Prior remediation chose docs + generator templates that callsuper.config().Impact
A developer who overrides
config()in a controller — a common, documented pattern — without callingsuper.config()silently ships that controller with CSRF disabled and no warning. The protection's presence is invisible and easy to drop.Suggested approach
This is a deliberate behavior change for every existing app, so it needs an upgrade-path design rather than an autonomous flip:
config()), orconfig()without callingsuper.config().Acceptance criteria
config()withoutsuper.config()either keeps CSRF active or emits a clear startup/dev warning.wheels upgrade checkadvisory exist.Source
Internal multi-agent framework review 2026-06-09, wave 2 (issues phase). Findings: security-pass SEC-10 (S5), followups SEC-10.