Skip to content

fix: remove \l/\U replacement case flags from names and flash keys (#4646) - #4652

Merged
bpamiri merged 3 commits into
developfrom
fix/adobe-u26-lower-upper-flags
Oct 28, 2026
Merged

bpamiri merged 3 commits into
developfrom
fix/adobe-u26-lower-upper-flags

Conversation

@bpamiri

@bpamiri bpamiri commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Refs #4646 (tracking issue — the \u sites landed in #4649; this covers the remaining \l/\U replacement case flags, and keeps the backreference audit open).

Adobe CF 2023 update 26 / 2025 update 14 parse the replacement case flags \l/\U as literal letters, so on patched builds:

  • hyphenize() turned wheelsIsAFramework into wheels-l-lis-la-l-lframework;
  • flash argument keys became lSuccess / lAlert instead of success / alert;
  • named route derivation produced adminUposts instead of adminPosts.

All three sites now use engine-neutral equivalents with no replacement-string case flags.

Changes

  • global/strings.cfm hyphenize() — removed the two \l flags. They are redundant: the function ends with LCase(), and the first pass inserts a hyphen before the lower-cased letter so the second pass's [a-z][A-Z] boundary never depends on that letter's case.
  • controller/redirection.cfc — the flash key is lowercased by hand: Len(item) > 5 mirrors the old pattern's required sixth character, CompareNoCase(Left(item, 5), "flash") == 0 mirrors ReReplaceNoCase's case-insensitive ^flash, and LCase(Mid(item, 6, 1)) & Mid(item, 7) mirrors the one-character capture.
  • mapper/matching.cfc — new public $ helper $upperCaseAfterComma() (same style as the merged $upperCaseAfterDash): trims leading/trailing comma runs, removes comma runs, upper-cases the following character when it matches the engine's own \w class, then the named-route code calls it. ReFind("^\w$", next) deliberately uses the same engine class as the legacy , +(\w) pattern, so non-ASCII behaviour stays per-engine (RustCFML matches é, Lucee 7 does not).

No public API change ($upperCaseAfterComma is internal; hyphenize() output unchanged; flash keys and route names produce pre-patch values).

Specs

  • global/stringsSpec.cfc — exact toBeWithCase cases for the brief's shapes (aB, aBcD, XMLHttpRequest, foo.Bar, a1B, foo_Bar, éclair, empty) plus a guarded differential against the old replacement when the case flags still parse.
  • controller/redirectionSpec.cfc — flashSuccess/flashAlert map to success/alert; an argument named exactly flash stays flash; FlashSuccess matches the prefix case-insensitively.
  • mapper/MatchingSpec.cfc — table for $upperCaseAfterComma (admin,posts, repeated/leading/trailing commas, digits, -show, a,_b, empty) plus a guarded differential against the old trim + \U + comma-removal sequence.
  • mapper/NestedResourcesSpec.cfc — the nested-resource integration path now asserts the exact name set captured from the pre-patch mapper (postComments, postComment, newPostComment, editPostComment) instead of a substring check.

Focused runs on Lucee 7: strings 23 passed, redirection 56, Matching 25, NestedResources 12; full core suite 7376 passed.

Patch-26 verification limitation

CI/local images are pre-patch (adobe@2023.0.11+330706, adobe@2025.0.06+331564), so u26/u14 cannot be executed here. Correctness is pinned by explicit expected outputs, the guarded differential (byte-identical to the legacy replacements on pre-patch engines), and a committed source guard (global/adobeU26LowerUpperSourceSpec.cfc) that fails if any of the three files reintroduces a \l/\U replacement case flag; confirmation on a patched Adobe build is still required. No backreference-only replacement (\1/\2 without case flags) is changed in this PR — the audit list from the brief stays open on #4646.

Verification

  • bash tools/test-local.sh (full core suite) → 7376 passed
  • tools/test-matrix.sh lucee7,adobe2023,adobe2025,boxlang sqlite → lucee7 7357, boxlang 7357, adobe2023 7351, adobe2025 7352 — all 0 failed / 0 errored
  • bash tools/rustcfml/run-suite.sh → RustCFML v0.742.2 7329 passed, 0 failed, 0 errors, 182 skipped
  • PR CI on head 3c8c1ad: all checks pass (adobe2023+sqlite, adobe2025 cold start, Adobe smokes, lucee7+h2, LuCLI, Tutorial E2E, RustCFML)

Adobe caught during verification

The first CI run failed every Adobe leg with HTTP 500 during engine start. Adobe's MID parameter validation rejects the two-argument form, so Mid(local.item, 7) in the flash-key rewrite broke onApplicationStart (FunctionArgMismatchException; every request 500). Fixed by passing Len(local.item) - 6 explicitly (commit 3c8c1ad); the resulting key is unchanged, and the Adobe legs pass locally and in CI.

Final head (9792f63): the flash-key tail now uses RemoveChars(local.item, 1, 6) (Adobe-safe, no Right(s,0)), with flash1/flash-/exact-flash cases in redirectionSpec; a new adobeU26LowerUpperSourceSpec pins the absence of \l/\U replacement flags in the three production files (it fails if they return); and the MatchingSpec differential corpus includes admin,éshow for the engine-specific \w behaviour. CI green on Lucee 6/7, Adobe 2023/2025 (cold start + smoke), BoxLang and RustCFML; patched u26/u14 still needs community confirmation.

Adobe CF 2023 update 26 / 2025 update 14 parse the \l and \U replacement case
flags as literal letters, so hyphenated route names became
"wheels-l-lis-la-l-lframework", flash keys became "lSuccess", and named route
derivation produced "adminUposts". #4649 fixed the \u sites; this covers the
remaining \l/\U sites.

- hyphenize(): drop the redundant \l flags - the final LCase already
  lower-cases the inserted letters and the hyphen insertion means the second
  pass never depends on the first pass's case.
- redirection.cfc: lower-case the first character after the flash prefix by
  hand (Len > 5 mirrors the old required sixth character; CompareNoCase
  mirrors the case-insensitive prefix).
- mapper/matching.cfc: add $upperCaseAfterComma() and use it for named routes;
  it trims comma runs, removes them, and upper-cases the following word
  character using the same engine \w class as the old pattern.

Output is identical on unpatched engines (guarded differential + explicit
expected outputs in the specs). No public API change.

Signed-off-by: Peter Amiri <peter@alurium.com>
Adobe's MID parameter validation rejects the two-argument form used in the
flash-key rewrite: onApplicationStart failed with FunctionArgMismatchException
and every request returned 500 on adobe2023/adobe2025. Pass Len(item) - 6
explicitly; the resulting key is unchanged.

Signed-off-by: Peter Amiri <peter@alurium.com>
Follow-up to the Adobe MID fix: prefer RemoveChars(item, 1, 6) over the counted
Mid tail (no zero-length slicing for the six-character "flashX" input), add the
flash1 and flash- cases to redirectionSpec, guard the three follow-up files
against reintroducing the \l/\U replacement case flags, and add non-ASCII
entries to the named-route differential corpus.

Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri
bpamiri merged commit a641d7a into develop Oct 28, 2026
27 checks passed
@bpamiri
bpamiri deleted the fix/adobe-u26-lower-upper-flags branch October 28, 2026 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant