From 2d5037abe16139ddfcfb2ea9da1eb1550136b27f Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 12 Jun 2026 23:53:09 +0000 Subject: [PATCH] fix(cli): strip {{enums}} placeholder in legacy CommandBox generator template The legacy CommandBox-module generator (cli/src/models/TemplateService.cfc) substituted {{belongsToRelationships}}, {{hasManyRelationships}}, {{hasOneRelationships}}, and {{validations}} but had no branch for the {{enums}} placeholder carried by its model template. box wheels generate model therefore leaked a literal {{enums}} into the generated config() body as invalid CFML. This legacy path has no enum codegen (unlike the LuCLI generator at cli/lucli/services/CodeGen.cfc), so the placeholder is now stripped unconditionally, mirroring the no-validations fallback. Adds a regression spec under cli/lucli/tests/specs/services/ that exercises the legacy TemplateService.processTemplate via makePublic and asserts the {{enums}} placeholder never survives into generated output. Refs #3180 Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> --- ...80-cli-legacy-template-enums-leak.fixed.md | 1 + .../LegacyTemplateServiceEnumsSpec.cfc | 50 +++++++++++++++++++ cli/src/models/TemplateService.cfc | 9 +++- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 changelog.d/3180-cli-legacy-template-enums-leak.fixed.md create mode 100644 cli/lucli/tests/specs/services/LegacyTemplateServiceEnumsSpec.cfc diff --git a/changelog.d/3180-cli-legacy-template-enums-leak.fixed.md b/changelog.d/3180-cli-legacy-template-enums-leak.fixed.md new file mode 100644 index 0000000000..e60e290b39 --- /dev/null +++ b/changelog.d/3180-cli-legacy-template-enums-leak.fixed.md @@ -0,0 +1 @@ +- CLI: the legacy CommandBox-module generator (`cli/src/models/TemplateService.cfc`) substituted `{{belongsToRelationships}}`, `{{hasManyRelationships}}`, `{{hasOneRelationships}}`, and `{{validations}}` but had no branch for the `{{enums}}` placeholder that ships in its model template, so `box wheels generate model` leaked a literal `{{enums}}` into the generated `config()` body as invalid CFML. This legacy path has no enum codegen (unlike the LuCLI generator), so the placeholder is now stripped unconditionally, matching the no-validations fallback (#3180) diff --git a/cli/lucli/tests/specs/services/LegacyTemplateServiceEnumsSpec.cfc b/cli/lucli/tests/specs/services/LegacyTemplateServiceEnumsSpec.cfc new file mode 100644 index 0000000000..2a7eb0815f --- /dev/null +++ b/cli/lucli/tests/specs/services/LegacyTemplateServiceEnumsSpec.cfc @@ -0,0 +1,50 @@ +/** + * Regression spec for the legacy CommandBox-module generator (`cli/src/`). + * + * The legacy model template (`cli/src/templates/ModelContent.txt`) carries a + * `{{enums}}` placeholder, but `cli/src/models/TemplateService.cfc::processTemplate` + * historically substituted only `{{belongsToRelationships}}`, `{{hasManyRelationships}}`, + * `{{hasOneRelationships}}`, and `{{validations}}` — there was no `{{enums}}` branch, so + * the literal placeholder leaked into the generated `config()` body as invalid CFML. + * + * The legacy path has no enum codegen, so the safe fix is to STRIP the placeholder + * (matching the no-validations fallback). This spec pins that behavior. + * + * See https://github.com/wheels-dev/wheels/issues/3180. + */ +component extends="wheels.wheelstest.system.BaseSpec" { + + function run() { + + describe("Legacy TemplateService — {{enums}} placeholder", () => { + + it("strips the {{enums}} placeholder so it never leaks into generated CFML", () => { + // Instantiate the legacy generator directly (resolves from the test webroot). + var svc = new cli.src.models.TemplateService(); + makePublic(svc, "processTemplate"); + + // Mirror the model template body that ships with the legacy module. + var template = 'component extends="Model" {' & chr(10) & + chr(9) & "function config() {" & chr(10) & + chr(9) & chr(9) & "{{belongsToRelationships}}" & chr(10) & + chr(9) & chr(9) & "{{hasManyRelationships}}" & chr(10) & + chr(9) & chr(9) & "{{hasOneRelationships}}" & chr(10) & + chr(9) & chr(9) & "{{validations}}" & chr(10) & + chr(9) & chr(9) & "{{enums}}" & chr(10) & + chr(9) & "}" & chr(10) & + "}"; + + var result = svc.processTemplate(template, {}); + + // The placeholder must be gone — a leaked {{enums}} is invalid CFML. + expect(result).notToInclude("{{enums}}"); + // Sanity: the sibling placeholders are still being handled (already stripped). + expect(result).notToInclude("{{validations}}"); + expect(result).notToInclude("{{belongsToRelationships}}"); + }); + + }); + + } + +} diff --git a/cli/src/models/TemplateService.cfc b/cli/src/models/TemplateService.cfc index 851cb683c2..b374ab9965 100644 --- a/cli/src/models/TemplateService.cfc +++ b/cli/src/models/TemplateService.cfc @@ -130,7 +130,14 @@ component { } else { processed = reReplace(processed, "\{\{validations\}\}", "", "all"); } - + + // Process enums — this legacy CommandBox-module generator has no enum codegen + // (unlike the LuCLI generator at cli/lucli/services/CodeGen.cfc). The model + // template still carries an {{enums}} placeholder, so strip it unconditionally; + // otherwise the literal {{enums}} leaks into the generated config() body as + // invalid CFML (issue ##3180). + processed = reReplace(processed, "\{\{enums\}\}", "", "all"); + // Process actions for controllers if (structKeyExists(arguments.context, "actions") && isArray(arguments.context.actions)) { var actionsCode = generateActionsCode(arguments.context.actions);