Skip to content

COLDBOX-1465 Resolve event-selected implicit views only from declared view folders - #725

Open
homestar9 wants to merge 3 commits into
ColdBox:developmentfrom
homestar9:fix/implicit-views-declared-roots
Open

homestar9 wants to merge 3 commits into
ColdBox:developmentfrom
homestar9:fix/implicit-views-declared-roots

Conversation

@homestar9

@homestar9 homestar9 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Description

With implicitViews enabled, a view chosen from the request's event name is resolved with the same search list as an explicit setView(). After views/ and viewsExternalLocation, that list also tries the application root and finally the bare view name. So ?event=tools.cleanup includes an existing /tools/cleanup.cfm from the app root, and for a handler with a missing action, ?event=main.legacy includes /main/legacy.cfm. These files are usually not views.

This PR limits views chosen from the event name to the declared view folders:

  • New Renderer.locateImplicitView( view, module = "" ).
    • It returns the located path only when the file is inside the conventions views/ folder or viewsExternalLocation. For a module event, it also accepts the module's views folder and the app's views/modules override folders.
    • Otherwise it returns "".
    • It compares normalized absolute paths, case-sensitively, so .. segments, and on a case-sensitive file system a folder whose name differs only by case, can't pass as a view folder. Symlinks inside a view folder keep working.
    • It reads a module's views folder from the current module config, so a module reload with a new viewsLocation takes effect. It adds no file system calls beyond the existing lookup.
  • HandlerService.isViewDispatch() uses it in both the module and non-module branches. This covers a missing handler and an existing handler with a missing action.
  • Renderer.discoverImplicitViews() uses it for an action that runs but sets no view. If no permitted view exists, it throws Renderer.ImplicitViewNotFoundException instead of including a root-level file. The same clear exception replaces the old missing-include error when the view doesn't exist anywhere.

Views named explicitly by handler code, with event.setView() or view(), are unchanged and keep the full search list.

No extra state passes between dispatch and rendering. locateView() and locateModuleView() always search the view folders before the app root, so a view accepted by locateImplicitView() is the same file rendering picks.

Breaking change: apps that relied on implicit views resolving to files outside their view folders must move those files into views/ or call setView() explicitly. Implicit views are opt-in since 8.3.0 (COLDBOX-1461), which limits who is affected. No existing spec relied on root-level implicit views, and all existing specs pass unchanged.

Open question (from the Jira issue): should this ship as-is in 8.x, behind a setting, or in 9.0? I'm happy to put it behind a setting if you prefer.

Jira Issues

Type of change

  • Improvement
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update (implicit views only resolve from view folders)

Checklist

  • My code follows the style guidelines of this project (cfformat run on touched files: no changes beyond the fix)
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (to follow once the approach is agreed)
  • I have added tests that prove my fix is effective:
    • Views in views/, external views, module views and module implicit views still dispatch.
    • Root-only .cfm files no longer dispatch, for a missing handler or a missing action.
    • An action that sets no view renders its views/ view.
    • Explicit setView() and view() calls to root-level views still work.
    • New tests/specs/integration/ImplicitViewsSpec.cfc, plus locateImplicitView() specs in RendererTest. Those also cover a missing view with an uppercase extension and a module whose viewsLocation changes.
    • New fixtures:
      • test-harness/rootonlyview.cfm, for a missing handler
      • test-harness/main/rootonlyaction.cfm, for a missing action. Both are lowercase because view dispatch lowercases the event, so on a case-sensitive file system the old lookup finds them too.
      • a test-harness/handlers/implicitViews.cfc handler with actions that set no view
      • views/implicitViews/withView.cfm, and the root-only implicitViews/noView.cfm, which the handler also selects explicitly
    • The specs toggle implicitViews on, since the harness leaves it off.
    • Without the change, the three root-only specs fail because the root templates are used. The five "still works" specs pass with and without the change.
    • The uppercase-extension and module-folder specs fail without the review follow-up and pass with it.
  • New and existing unit tests pass locally with my changes:
    • runner.cfm and runner-integration.cfm, compared with a run of development without the change.
    • No new failures, and the new specs pass, on Lucee 5.4, Adobe 2023, Adobe 2025, BoxLang 1 and BoxLang 1 with CFML compat. Integration runner 105/105 on each engine.
    • After the review follow-up commits, the same runs again: integration 105/105 and no new mvc failures on each engine, apart from the disk store specs below.
    • On Adobe, two unrelated specs errored once each: CacheBoxWithDiskSoreTest, which also fails intermittently without this change, and BuilderTest.testbuildfeed, which fetches a live RSS feed ("Unable to read the source URL"). In the follow-up run, CacheBoxWithDiskSoreTest and DiskStoreTest errored on Lucee and Adobe with Can't delete file [tests/tmp/cacheDepot], a local file lock unrelated to this change, and one ScopeWiringConcurrencyTest spec errored once on Adobe 2023.
    • MySQL wasn't available locally, so ORM was turned off and the DB log appender removed for these runs.

@homestar9
homestar9 marked this pull request as ready for review October 16, 2026 19:21
Copilot AI balanced review requested due to automatic review settings October 16, 2026 19:21
@homestar9
homestar9 force-pushed the fix/implicit-views-declared-roots branch from 66e8e89 to d01701c Compare October 16, 2026 19:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A containment bypass, stale module roots, missing-file acceptance, and ineffective regression fixtures remain unresolved.

4 open findings
What changed in this PR

Restricts ColdBox’s event-selected implicit views to declared view folders while preserving explicit view selection.

Changes:

  • Adds restricted lookup and a clear missing-implicit-view exception.
  • Applies restricted lookup during dispatch and rendering.
  • Adds lookup tests, integration tests, and template fixtures.
File Description
tests/​specs/​web/​RendererTest.cfc Tests restricted view lookup.
tests/​specs/​integration/​ImplicitViewsSpec.cfc Tests implicit dispatch and explicit selection.
test-harness/​views/​implicitViews/​withView.cfm Provides an allowed implicit view.
test-harness/​rootOnlyView.cfm Provides a root-only missing-handler fixture.
test-harness/​main/​rootOnlyAction.cfm Provides a root-only missing-action fixture.
test-harness/​implicitViews/​noView.cfm Provides an explicit-only root template.
test-harness/​handlers/​implicitViews.cfc Adds implicit and explicit rendering actions.
system/​web/​services/​HandlerService.cfc Restricts implicit view dispatch.
system/​web/​Renderer.cfc Adds folder containment checks and lookup validation.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread system/web/Renderer.cfc Outdated
Comment thread system/web/Renderer.cfc Outdated
Comment thread system/web/Renderer.cfc Outdated
Comment thread tests/specs/integration/ImplicitViewsSpec.cfc Outdated
… declared view folders

Views chosen from the request's event name used the same search list as explicit views, which also tries the application root and the bare view path, so any template under the app root matching an event could be included as a view. The new Renderer.locateImplicitView() only accepts views in the views folder, the external views location or a module's views folders. isViewDispatch() uses it for missing handlers and actions, and discoverImplicitViews() throws Renderer.ImplicitViewNotFoundException instead of rendering a template outside those folders. Explicit setView()/view() calls are unchanged.
@homestar9
homestar9 force-pushed the fix/implicit-views-declared-roots branch from d01701c to 0f3e318 Compare October 16, 2026 19:30
- Compare view folders case-sensitively
- Treat a view that locateView() returns unchanged as not found, also with an uppercase extension
- Read a module's views folder from its current config, so a module reload takes effect
- Lowercase the root-only fixtures: view dispatch lowercases the event, so on a case-sensitive file system the old lookup missed them
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants