fix(dispatch): replace Lucee-only bare cfabort with abort in public-component 404 branch - #3037
Conversation
…omponent 404 branch The enablePublicComponent=false anti-fingerprinting block in Dispatch.cfc ended with a bare 'cfabort;' statement. That tag-in-script form is Lucee-only: every Adobe engine compiles it as a reference to an undefined variable and throws 'Variable CFABORT is undefined' at runtime. Since the stock app homepage is served by the public component, GET / on a stock Adobe install in testing/production returned HTTP 500 (composite 'Not Found' + error template) on every request instead of a clean 404. - One-token fix: 'cfabort;' -> script keyword 'abort;'. - New structural guard spec (BareCfabortGuardSpec) scans vendor/wheels/**/*.cfc for bare script-context cfabort statements with line-anchored matching (comment-only lines skipped; tag-context cfabort in .cfm/tag-based CFCs stays legal) and fails on any hit. The branch has no execution coverage on any engine (an executed abort would kill the test runner), so the structural scan is the gate. - PublicComponentProductionSpec: note the branch now ends in 'abort;' and widen the source-inspection window past the new comments. - CLAUDE.md: new Cross-Engine Invariant 13 documenting the bare tag-in-script statement trap. Fixes #3029 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 PR replaces the Lucee-only bare cfabort; statement in Dispatch.cfc's enablePublicComponent=false anti-fingerprinting branch with the cross-engine script keyword abort;, and adds a structural guard spec so the bare form can never reappear under vendor/wheels/**/*.cfc. The one-token fix is correct, the live Adobe 2023 before/after evidence (500 → 404) is credible, the TDD red→green sequence for the guard spec is documented, and the commit, changelog fragment, and CLAUDE.md invariant are all in order. Verdict: comment — the diff is sound; the only findings are one docs gap and one non-blocking robustness note on the guard spec.
Correctness
No findings. Verification performed against head c5caa5730:
vendor/wheels/Dispatch.cfc:404now readsabort;, which is valid CFScript on Lucee 5/6/7, Adobe CF 2018–2025, and BoxLang. The surrounding 404 contract (cfheader(statuscode=404)→cfcontent→writeOutput("Not Found")→abort;) is intact and still satisfies the issue #2233 anti-fingerprinting requirement.- I grepped every remaining
cfabortoccurrence undervendor/wheelsand checked each against the guard's skip rules:Dispatch.cfc:395,398andcontroller/rendering.cfc:86are comment-only lines (skipped by the trimmed-prefix check),public/views/cli.cfm:1085andwheelstest/system/util/XMLConverter.cfc:255are tag-form<cfabort>(no trailing semicolon, preceded by an angle bracket — never matches(^|[\s;{}])cfabort\s*;), andPublicComponentProductionSpec.cfc:211has the token followed by a word, not a semicolon. The guard passes today for the right reasons, not by accident. - The widened 800-char inspection window in
PublicComponentProductionSpec.cfc:206is sufficient: the distance from the!application.wheels.enablePublicComponentgate tostatuscode=404is roughly 530 characters including the new explanatory comments, leaving ~270 chars of headroom. - I also swept
vendor/wheelsfor sibling bare tag-in-script statements (cfflush;,cfexit;,cfrethrow;, etc.) — none exist, so the guard'scfabort-only scope leaves no latent instance of the same bug class uncovered today.
Cross-engine
No findings. The new spec itself was checked against the documented invariants: Left() is only called with constant lengths 1–2 (invariant 8 concerns length 0), the closures declare all state with var inside the it() body (no outer-local capture), # characters in string literals are escaped as ##3029, and no inline-closure-as-constructor-arg shape appears. The PR's own test evidence covers Lucee 7 (full suite, +1 pass) and Adobe 2023 (both touched security bundles green), which matches the CLAUDE.md cross-engine verification bar for an Adobe-targeted fix.
Tests
Non-blocking robustness note on vendor/wheels/tests/specs/security/BareCfabortGuardSpec.cfc:72-75: the comment-skip only handles comment-only lines —
if (Left(trimmed, 2) == "//" || Left(trimmed, 1) == "*" || Left(trimmed, 2) == "/*") {
continue;
}
if (REFindNoCase(pattern, trimmed)) {A future code line with a trailing comment containing the literal token plus semicolon (e.g. abort; // was cfabort;) would false-positive, because the regex runs over the full trimmed line. No such line exists today (verified by grep), and the failure mode is a loud, self-describing test failure rather than a shipped bug, so this does not block. If you want to harden it cheaply: strip a trailing //-to-end-of-line segment from trimmed before the REFindNoCase, keeping the existing whole-line skips. Equally fine to leave as-is.
Otherwise the test work is solid: BDD spec extending wheels.WheelsTest (not RocketUnit), documented red→green (417 with offender path → 200), self-exclusion plus token-by-concatenation so the spec can't flag itself, and a sensible rationale for why structural scanning replaces execution coverage (an executed abort would kill the runner).
Docs
.ai/wheels/cross-engine-compatibility.md was not updated. Every recent sibling of the new CLAUDE.md invariant has a parallel deep-reference section in that file — invariant 10 (attributeCollection, line 303), invariant 11 (local in catch, line 252), invariant 12 (for in finally, line 173) — and CLAUDE.md's Cross-Engine Invariants section explicitly points there as the deep reference. Please add a short "Bare tag-in-script statements (Adobe CF)" section mirroring the new invariant 13: the WRONG/RIGHT snippet (cfabort; vs abort;), the Adobe Variable CFABORT is undefined failure mode, and a pointer to BareCfabortGuardSpec.cfc and issue #3029. This is the only change I'm asking for.
Everything else checks out: changelog fragment changelog.d/3029-dispatch-bare-cfabort-adobe.fixed.md follows the <slug>.<type>.md convention with a complete bullet (no direct CHANGELOG.md edit), and the CLAUDE.md invariant entry is accurate and appropriately scoped.
Commits
No findings. Single commit c5caa5730: valid type/scope (fix(dispatch)), header 89 chars (≤ 100), body lines within limits, explains the "why" (Adobe variable-reference compilation of the bare token), references the issue, and carries both the DCO Signed-off-by trailer and co-author credit.
What
vendor/wheels/Dispatch.cfc'senablePublicComponent=falseanti-fingerprinting 404 branch (issue #2233) ended in a barecfabort;. That statement form is Lucee-only tag-in-script syntax — every Adobe engine compiles it as a reference to an undefined variable and throwsVariable CFABORT is undefinedat runtime. Because the stock app's homepage is served by the public component,GET /on a stock Adobe install intesting/productionhit this branch on every request and returned HTTP 500 (compositeNot Found+ error template) instead of a clean404. Root cause of discussion #3023.Changes
cfabort;→ script keywordabort;(Dispatch.cfc:404), with a comment explaining why the bare form is forbidden.vendor/wheels/tests/specs/security/BareCfabortGuardSpec.cfc: scansvendor/wheels/**/*.cfcfor bare script-contextcfabortstatements with line-anchored matching (comment-only lines skipped per Anti-Pattern 14 spirit — no whole-file non-greedy comment-strip regex, which hangs Lucee 7; tag-context<cfabort>in .cfm/tag-based CFCs stays legal). The branch has zero execution coverage on any engine (an executed abort would kill the test runner), so the structural scan is the regression gate. The forbidden token is built by concatenation so the spec can never flag itself.abort;and why; widens the source-inspection window (400→800 chars) past the new explanatory comments.fixed).Test evidence
TDD red→green (guard spec, Lucee 7 + SQLite docker harness):
417—Found bare tag-in-script 'cfabort' statement(s) at: /Dispatch.cfc:400200— 1 pass / 0 fail / 0 errorLive verification,
environment=testing(acceptance gate):Not Found+ error template containingVariable CFABORT is undefined(×2)Not Found(9 bytes, no error template)Not Found(9 bytes)Not Found(10 bytes)Not Found(10 bytes)Suites:
internal.testClientSpeccontainer artifacts on both runs).BareCfabortGuardSpec+PublicComponentProductionSpec): 46 pass / 0 fail / 0 error.Fixes #3029
🤖 Generated with Claude Code