From d1d9f863e0db01263be17f540316ff26df7bd9b4 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:21:18 +0000 Subject: [PATCH 1/3] fix(config): interpolate plugin and package names in load-trace log messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Loading plugin..." / "Loading package..." INFO lines in wheels_security.log were emitting literal placeholder tokens (`#arguments.dirName#`, `#local.pluginKey#`, etc.) instead of the resolved values. The `WriteLog(text="...")` strings escaped each `#` as `##` — CFML's way to write a literal `#` — so the placeholders never ran through expression evaluation and the log was useless for diagnosing which plugin or package was being loaded. Drop the escaping so single-pound interpolation kicks in. Add a spec under `vendor/wheels/tests/specs/packages/` that inspects both call sites and fails fast if the escape pattern reappears. Fixes #2630 Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> --- CHANGELOG.md | 1 + vendor/wheels/PackageLoader.cfc | 2 +- vendor/wheels/Plugins.cfc | 2 +- .../PackageLoaderLogInterpolationSpec.cfc | 60 +++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ee482e6ed..c7c4845bff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ All historical references to "CFWheels" in this changelog have been preserved fo - Use the Adobe-safe 3-argument `mid()` form when stripping the `wheels` prefix in the MCP command executor and its security spec; the prior 2-arg call crashed the entire `security/` test bundle on Adobe ColdFusion (#2613) - Replace Lucee-only `directoryCreate(path, true)` calls in `BrowserTest.$captureFailureArtifacts` and `McpServer` test-file generation with `java.io.File.mkdirs()` so artifact directory creation no longer trips Adobe ColdFusion's `DIRECTORYCREATE` single-argument validator (#2614) - Generated `Application.cfc` (and the in-repo `public/`, `examples/tweet/`, `examples/starter-app/` copies) now assigns the injector directly to `application.wheelsdi` in `onApplicationStart()` and `onError()` instead of an orphan local `injector` variable, matching the documented 4.0 DI container name and the way every other reference in the file reads (#2622) +- Interpolate plugin and package names in the "Loading plugin..." / "Loading package..." `wheels_security.log` INFO lines so operators can read which plugin/package was being loaded; the call sites were double-escaping the pound signs (`##var##`) and emitting literal `#var#` placeholders instead of resolved values (#2630) ---- diff --git a/vendor/wheels/PackageLoader.cfc b/vendor/wheels/PackageLoader.cfc index 8cbc28fe86..8796b20adf 100644 --- a/vendor/wheels/PackageLoader.cfc +++ b/vendor/wheels/PackageLoader.cfc @@ -366,7 +366,7 @@ component output="false" { try { WriteLog( - text = "[Wheels] Loading package '##arguments.dirName##' from ##arguments.pkgDir##", + text = "[Wheels] Loading package '#arguments.dirName#' from #arguments.pkgDir#", type = "information", file = "wheels_security" ); diff --git a/vendor/wheels/Plugins.cfc b/vendor/wheels/Plugins.cfc index 9d226dbeb6..2d7b662778 100644 --- a/vendor/wheels/Plugins.cfc +++ b/vendor/wheels/Plugins.cfc @@ -141,7 +141,7 @@ component output="false" extends="wheels.Global"{ local.pluginValue = local.plugins[local.pluginKey]; try { WriteLog( - text = "[Wheels] Loading plugin '##local.pluginKey##' from ##local.pluginValue.folderPath##", + text = "[Wheels] Loading plugin '#local.pluginKey#' from #local.pluginValue.folderPath#", type = "information", file = "wheels_security" ); diff --git a/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc b/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc new file mode 100644 index 0000000000..a885f318d7 --- /dev/null +++ b/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc @@ -0,0 +1,60 @@ +/** + * GH#2630: the "Loading plugin..." / "Loading package..." INFO lines in + * wheels_security.log emitted literal placeholder tokens + * (`#arguments.dirName#`, `#local.pluginKey#`, etc.) instead of the resolved + * values, because the `WriteLog(text="...")` strings escaped each `#` as + * `##` — CFML's escape sequence for a literal `#`. + * + * This spec inspects the call sites in `vendor/wheels/PackageLoader.cfc` and + * `vendor/wheels/Plugins.cfc` and asserts the malformed `##var##` patterns + * are gone, so a future regression that re-introduces them fails fast + * without anyone having to grep the log file. + */ +component extends="wheels.WheelsTest" { + + function run() { + describe("Plugin & package loader log interpolation (##2630)", () => { + + it("does not escape pounds in the PackageLoader load-trace message", () => { + var source = FileRead(ExpandPath("/wheels/PackageLoader.cfc")); + + // "####" in a CFML string literal evaluates to the two-char + // sequence "##", so this search looks for the literal four + // characters "####" in the source. That four-char sequence is + // CFML's escape for "##" in a runtime string — the bug. + expect(FindNoCase("####arguments.dirName####", source) GT 0).toBeFalse( + "PackageLoader.cfc must not emit escaped pounds around " + & "arguments.dirName in WriteLog text — CFML reads each " + & "doubled pound as a literal pound, so the placeholder " + & "never interpolates. Use a single pound on each side. " + & "See issue ##2630." + ); + + expect(FindNoCase("####arguments.pkgDir####", source) GT 0).toBeFalse( + "PackageLoader.cfc must not emit escaped pounds around " + & "arguments.pkgDir in WriteLog text — see issue ##2630." + ); + }); + + it("does not escape pounds in the Plugins load-trace message", () => { + var source = FileRead(ExpandPath("/wheels/Plugins.cfc")); + + expect(FindNoCase("####local.pluginKey####", source) GT 0).toBeFalse( + "Plugins.cfc must not emit escaped pounds around " + & "local.pluginKey in WriteLog text — CFML reads each " + & "doubled pound as a literal pound, so the placeholder " + & "never interpolates. Use a single pound on each side. " + & "See issue ##2630." + ); + + expect(FindNoCase("####local.pluginValue.folderPath####", source) GT 0).toBeFalse( + "Plugins.cfc must not emit escaped pounds around " + & "local.pluginValue.folderPath in WriteLog text — see " + & "issue ##2630." + ); + }); + + }); + } + +} From 0988de8352194523cb2315dc90b9a6f2ea10b2f1 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:37:50 +0000 Subject: [PATCH 2/3] fix(packages): address Reviewer A/B consensus findings (round 1) - Collapse the 13-line block comment at the top of vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc to a single line per CLAUDE.md's "one short line max" rule. - Add positive assertions in both it() blocks verifying the correct single-pound pattern is present in PackageLoader.cfc and Plugins.cfc so the spec stays meaningful if the WriteLog line is later removed. Refs PR #2641, issue #2630. Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> --- .../PackageLoaderLogInterpolationSpec.cfc | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc b/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc index a885f318d7..7a06275cd1 100644 --- a/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc +++ b/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc @@ -1,15 +1,4 @@ -/** - * GH#2630: the "Loading plugin..." / "Loading package..." INFO lines in - * wheels_security.log emitted literal placeholder tokens - * (`#arguments.dirName#`, `#local.pluginKey#`, etc.) instead of the resolved - * values, because the `WriteLog(text="...")` strings escaped each `#` as - * `##` — CFML's escape sequence for a literal `#`. - * - * This spec inspects the call sites in `vendor/wheels/PackageLoader.cfc` and - * `vendor/wheels/Plugins.cfc` and asserts the malformed `##var##` patterns - * are gone, so a future regression that re-introduces them fails fast - * without anyone having to grep the log file. - */ +// GH##2630: guard the PackageLoader / Plugins load-trace WriteLog calls against re-escaping #var# as ##var##. component extends="wheels.WheelsTest" { function run() { @@ -34,6 +23,19 @@ component extends="wheels.WheelsTest" { "PackageLoader.cfc must not emit escaped pounds around " & "arguments.pkgDir in WriteLog text — see issue ##2630." ); + + // Positive assertions: lock the single-pound form in place so a + // future refactor that silently removes the log line fails the + // test instead of staying green. + expect(FindNoCase("'##arguments.dirName##'", source) GT 0).toBeTrue( + "PackageLoader.cfc must retain single-pound interpolation " + & "around arguments.dirName in WriteLog text — see issue ##2630." + ); + + expect(FindNoCase("from ##arguments.pkgDir##", source) GT 0).toBeTrue( + "PackageLoader.cfc must retain single-pound interpolation " + & "around arguments.pkgDir in WriteLog text — see issue ##2630." + ); }); it("does not escape pounds in the Plugins load-trace message", () => { @@ -52,6 +54,17 @@ component extends="wheels.WheelsTest" { & "local.pluginValue.folderPath in WriteLog text — see " & "issue ##2630." ); + + // Positive assertions: see PackageLoader block above for rationale. + expect(FindNoCase("'##local.pluginKey##'", source) GT 0).toBeTrue( + "Plugins.cfc must retain single-pound interpolation around " + & "local.pluginKey in WriteLog text — see issue ##2630." + ); + + expect(FindNoCase("from ##local.pluginValue.folderPath##", source) GT 0).toBeTrue( + "Plugins.cfc must retain single-pound interpolation around " + & "local.pluginValue.folderPath in WriteLog text — see issue ##2630." + ); }); }); From 6ee27fef8740e5b5c370ddc47038611cb1ef405b Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:48:08 +0000 Subject: [PATCH 3/3] fix(packages): address Reviewer A/B consensus findings (round 2) - Collapse the 4-line escaping-logic comment in the PackageLoader it() block of vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc to a single line per CLAUDE.md's "one short line max" rule. - Collapse the 3-line positive-assertion comment in the same it() block to a single line: "Positive guard: fails if the WriteLog line is silently removed." The single-line comment in the Plugins it() block was already compliant. Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> --- .../specs/packages/PackageLoaderLogInterpolationSpec.cfc | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc b/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc index 7a06275cd1..5f126073f9 100644 --- a/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc +++ b/vendor/wheels/tests/specs/packages/PackageLoaderLogInterpolationSpec.cfc @@ -7,10 +7,7 @@ component extends="wheels.WheelsTest" { it("does not escape pounds in the PackageLoader load-trace message", () => { var source = FileRead(ExpandPath("/wheels/PackageLoader.cfc")); - // "####" in a CFML string literal evaluates to the two-char - // sequence "##", so this search looks for the literal four - // characters "####" in the source. That four-char sequence is - // CFML's escape for "##" in a runtime string — the bug. + // "####" in a CFML string literal evaluates to "##" at runtime — the bug form. expect(FindNoCase("####arguments.dirName####", source) GT 0).toBeFalse( "PackageLoader.cfc must not emit escaped pounds around " & "arguments.dirName in WriteLog text — CFML reads each " @@ -24,9 +21,7 @@ component extends="wheels.WheelsTest" { & "arguments.pkgDir in WriteLog text — see issue ##2630." ); - // Positive assertions: lock the single-pound form in place so a - // future refactor that silently removes the log line fails the - // test instead of staying green. + // Positive guard: fails if the WriteLog line is silently removed. expect(FindNoCase("'##arguments.dirName##'", source) GT 0).toBeTrue( "PackageLoader.cfc must retain single-pound interpolation " & "around arguments.dirName in WriteLog text — see issue ##2630."