Repository navigation
review remediation: wave-3 deferred subsystem cleanups — rendering coercion/Chr(7), MSSQL/Oracle adapters, EventMethods memo, route-tester XSS (held: PRs #2900/2901/2908/2909 now merged) #2961
Description
Problem
A set of deferred subsystem cleanups that were held out of wave 2 because their files were occupied by then-open PRs. Those PRs (#2900, #2901, #2908, #2909) have since merged, so all of this is now unblocked. Grouped here to bound the issue count; each section is independently actionable. Line numbers verified against origin/develop @ 8971094.
A. controller/rendering.cfc (was held behind PR #2901)
- C2 [Medium, robustness]
renderWith()JSON coercion —vendor/wheels/controller/rendering.cfc:275-306: the coercion gate isStructCount(arguments) > 8against an 8-name$definedlist, butrenderWithnow declares 9 params, sostatusleaks into coercion args (stale arity check).Replace(content, Chr(7), "", "all")(:287, :293-295) deletes legitimate BEL bytes anywhere in the serialized payload (data corruption). The force-to-integer regex rewrites serialized JSON by key name across nested structures, hitting nested same-named keys, with a full-payload regex/replace pass per key (perf). Fix: build the param-exclusion list from function metadata; coerce the data structure before serialization instead of regex-editing serialized JSON. Note the Chr(7) marker is a deliberate cross-engineSerializeJSONworkaround — validate any pre-serialization cast across engines. - C13 [Low, robustness]
$includeFilesilently blanks columns —:653-657:catch(any e){ arguments[property]="" }masks unreadable/binary-column read errors as blank values with no logging. Fix: narrow the catch or log before defaulting. - C16 [Low, perf]
$includeFilere-tokenizes the column list per row —:621, :649, :653-657:ListToArray(query.columnList)runs inside both per-row loops. Fix: hoist above the loops. - C17 [Low, perf]
$getStatusCodes()rebuilds a 63-entry constant struct per render —:809-876: reached from every render path;local.statusTextcomputed then unused in the numeric branch;$returnStatusCodedoesStructFindValueover the freshly rebuilt struct. Fix: memoize in application scope, add a reverse map for value lookup.
B. DB adapters (was held behind PR #2908)
- DA1 [Low, quality] GROUP BY dropped from paginated SQL Server queries —
vendor/wheels/databaseAdapters/MicrosoftSQLServer/MicrosoftSQLServerModel.cfc:191-192:local.afterWhere = "GROUP BY ..."is overwritten by a plain=assignment of"ORDER BY ..."(should be&=). Currently latent (the onlylimit>0caller never forwardsgroup). Fix:=→&=; add afindAll(group=..., page=...)SQL Server spec. - DA5 [Medium, quality] Oracle
$randomOrderreturnsRANDOM(), invalid on Oracle —vendor/wheels/databaseAdapters/Oracle/OracleModel.cfc:226-227:ORDER BY RANDOM()raises ORA-00904; the legacy adapter returneddbms_random.value().findAll(order="random")throws on Oracle; soft-fail CI hides it. Fix: returnDBMS_RANDOM.VALUE; add an Oracle random-order test. - DA14 [Low, perf] SQL Server pagination re-strips the whole SELECT list per ORDER BY column (O(n²)) —
MicrosoftSQLServerModel.cfc:116-199:$stripIdentifierQuotes(thirdSelect)recomputed inside the per-column loop;ListGetAt/ListFindover growing lists. Fix: hoist the strip; convert lists to arrays once.
C. events/EventMethods.cfc (was held behind PR #2900)
- DC6 [Low, quality/perf] Misspelled memo guard forces a second
GetHTTPRequestData()body materialization per request —vendor/wheels/events/EventMethods.cfc:173-174: the guard checks the singular key$wheelsHeader(written nowhere) while the next line writes the plural$wheelsHeaders, so the memo never hits;$initializeRequestScopealready stored the full result inrequest.wheels.httpRequestData. Fix: fix the guard key spelling and reuse the already-stored headers.
D. public/helpers.cfm route tester (was held behind PR #2909)
- SEC-8 / P9 [Medium, security] Reflected XSS in the verb-mismatch message —
vendor/wheels/public/helpers.cfm:226: rawarguments.pathis interpolated into the verb-mismatch message whilerequestMethodis encoded; sinks atroutetester.cfm:19androutetesterprocess.cfm:43. Fix: wraparguments.pathinEncodeForHTML(line 237 already does so for the RouteNotFound message — line 228 does not). - P14 [Low, perf] Route tester always runs a second full regex scan —
helpers.cfm:198-228: the alternatives loop (consumed only whenmatchesis empty) runs unconditionally on every invocation, including lazy.regexwrites onto application-scope route structs from a request thread. Fix: hoist the alternatives loop into the no-match branch, matchingDispatch.cfc's structure.
Impact
C2's Chr(7) strip silently corrupts JSON payloads; DA5 makes order="random" throw on Oracle (hidden by soft-fail CI); SEC-8 is a reflected-XSS in the dev route tester; the rest are latent correctness/perf cleanups.
Suggested approach
Address per section above. SEC-8 and C2 (security + data-integrity) should lead; the perf/quality items can follow. Run bash tools/test-local.sh plus a matrix spot-check (Adobe 2023 + Lucee 7); add the Oracle and SQL Server specs noted.
Acceptance criteria
-
renderWithexcludes all framework params via metadata; JSON payloads are not regex-edited post-serialization; BEL bytes in data survive (cross-engine verified). - MSSQL paginated GROUP BY is retained (spec added); Oracle
order="random"works (spec added); MSSQL pagination strip hoisted out of the loop. - EventMethods memo guard fixed; no second
GetHTTPRequestData()per request. - Route-tester verb-mismatch message HTML-encodes
arguments.path; the alternatives scan runs only on no-match.
Source
Internal multi-agent framework review 2026-06-09, wave 2 (issues phase). Held packages (occupying PRs #2900/#2901/#2908/#2909 now merged): controller C2/C13/C16/C17, db-adapters DA1/DA5/DA14, dispatch-core DC6, public-ui P9/P14, security-pass SEC-8.
Activity
wheels-bot commentedon Jun 10, 2026
Wheels Bot — Triage
Classified as bug.
Suspected layer
controller— see.ai/wheels/controllers/for canonical patterns. Note: this roll-up actually spans four subsystems (controller rendering, DB adapters, dispatch events, public route-tester helpers);controlleris the primary because C2 (renderWithJSON coercion) is the lead data-integrity item. Downstream stages should also consult.ai/wheels/cross-engine-compatibility.mdfor the Chr(7)SerializeJSONworkaround and treat the four sections as independently addressable.Fix sketch
Nine concrete fixes across four files. A.
vendor/wheels/controller/rendering.cfc— rebuild the param-exclusion list from function metadata sorenderWith's 9thstatusparam stops leaking into coercion (C2:275-306); coerce the data structure beforeSerializeJSONinstead ofReplace(..., Chr(7), "")and the by-key regex pass on the serialized payload (cross-engine validate); narrow/log$includeFile's blanket catch (C13:653-657); hoistListToArray(query.columnList)above the per-row loops (C16:621,649); memoize$getStatusCodes()in application scope with a reverse-value map (C17:809-876). B. DB adapters —MicrosoftSQLServer/MicrosoftSQLServerModel.cfc:191-192swap=to&=so paginated GROUP BY survives (DA1);Oracle/OracleModel.cfc:226-227returnDBMS_RANDOM.VALUEinstead ofRANDOM()(DA5); hoist$stripIdentifierQuotes(thirdSelect)out of the per-ORDER-BY-column loop and pre-convert lists to arrays inMicrosoftSQLServerModel.cfc:116-199(DA14). C.vendor/wheels/events/EventMethods.cfc:173-174— fix the singular-vs-plural memo key ($wheelsHeader→$wheelsHeaders) and reuserequest.wheels.httpRequestData(DC6). D.vendor/wheels/public/helpers.cfm— wraparguments.pathinEncodeForHTMLat line 226 to match line 237 (SEC-8); hoist the alternatives regex loop into the no-match branch to matchDispatch.cfc's structure (P14:198-228).Confidence:
mediumEach finding is mechanically clear with a named file:line and concrete fix shape, but the cross-engine auto-downgrade fires (C2's Chr(7) strip is a deliberate
SerializeJSONworkaround that must be re-validated across Lucee/Adobe/BoxLang); the multi-subsystem scope and a reflected-XSS item (SEC-8) also warrant the propose-fix safety net.<!-- wheels-bot:triage:2961 -->
<!-- wheels-bot:triage-class:bug -->
<!-- wheels-bot:triage-confidence:medium -->
wheels-bot commentedon Jun 10, 2026
Wheels Bot — Fix on hold for human review
The proposed fix touches sensitive areas (
vendor/wheels/controller/rendering.cfcJSON-coercion path,vendor/wheels/public/helpers.cfmreflected-XSS) and the bot's safety net requires a human in the loop before any code is written.Specifically:
Chr(7)wrap/strip atrendering.cfc:287,293-295is a deliberateSerializeJSONworkaround. The triage already flagged this — re-validation across Lucee 5/6/7, Adobe CF 2018/2021/2023/2025, and BoxLang is required before changing the coercion strategy. The "coerce data before serialization" approach proposed in the fix sketch needs a human to verify each engine'sSerializeJSONactually preserves string vs. integer typing without the BEL-marker round-trip.arguments.pathinEncodeForHTMLathelpers.cfm:226is mechanically simple, but the surrounding route-tester rendering path (routetester.cfm:19,routetesterprocess.cfm:43) should be audited for sibling untrusted-input sinks before a one-line patch lands in isolation.Suggested split for whoever picks this up:
The DC6 (EventMethods memo guard typo) and DA1/DA5 (MSSQL
=→&=, OracleRANDOM()→DBMS_RANDOM.VALUE) items are the smallest and safest starting points if a human wants to land something incrementally.fix(public): encode path in route-tester verb-mismatch message, scan …
perf(events): fix $wheelsHeaders memo guard and reuse stored httpRequ…
fix(controller): coerce renderWith JSON types pre-serialization, stop…
fix(controller): rendering residuals C13/C16/C17 and $expandedAssocia…