Skip to content

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

@bpamiri

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 coercionvendor/wheels/controller/rendering.cfc:275-306: the coercion gate is StructCount(arguments) > 8 against an 8-name $defined list, but renderWith now declares 9 params, so status leaks 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-engine SerializeJSON workaround — validate any pre-serialization cast across engines.
  • C13 [Low, robustness] $includeFile silently 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] $includeFile re-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.statusText computed then unused in the numeric branch; $returnStatusCode does StructFindValue over 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 queriesvendor/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 only limit>0 caller never forwards group). Fix: =&=; add a findAll(group=..., page=...) SQL Server spec.
  • DA5 [Medium, quality] Oracle $randomOrder returns RANDOM(), invalid on Oraclevendor/wheels/databaseAdapters/Oracle/OracleModel.cfc:226-227: ORDER BY RANDOM() raises ORA-00904; the legacy adapter returned dbms_random.value(). findAll(order="random") throws on Oracle; soft-fail CI hides it. Fix: return DBMS_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/ListFind over 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 requestvendor/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; $initializeRequestScope already stored the full result in request.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 messagevendor/wheels/public/helpers.cfm:226: raw arguments.path is interpolated into the verb-mismatch message while requestMethod is encoded; sinks at routetester.cfm:19 and routetesterprocess.cfm:43. Fix: wrap arguments.path in EncodeForHTML (line 237 already does so for the RouteNotFound message — line 228 does not).
  • P14 [Low, perf] Route tester always runs a second full regex scanhelpers.cfm:198-228: the alternatives loop (consumed only when matches is empty) runs unconditionally on every invocation, including lazy .regex writes onto application-scope route structs from a request thread. Fix: hoist the alternatives loop into the no-match branch, matching Dispatch.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

  • renderWith excludes 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions