Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/scaffold-homepage-onboarding.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- `wheels new` generates a richer default home page: a runtime status line (Wheels version, engine, database, environment) plus a Next-steps command guide, replacing the bare two-line placeholder — surfacing the onboarding content from the redesigned framework welcome page where users actually land (#2098)
1 change: 1 addition & 0 deletions changelog.d/wheels-welcome-production-gate.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- The `/wheels` welcome page now defense-in-depth gates itself with `$blockInProduction()` like every other `Public` handler, so it no longer renders outside `development` when `enablePublicComponent` is manually enabled — closing a version/engine/database/environment disclosure gap (reverses the #2233 exception)
15 changes: 14 additions & 1 deletion cli/lucli/Module.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -6191,7 +6191,20 @@ component extends="modules.BaseModule" {

fileWrite(
targetDir & "/app/views/main/index.cfm",
'<h1>Welcome to ' & appName & '</h1>' & nl & '<p>Your Wheels application is running. Edit this file at app/views/main/index.cfm</p>' & nl
(
'<cfoutput>' & nl &
'<h1>Welcome to ' & appName & '</h1>' & nl &
'<p>Your <strong>Wheels ##get("version")##</strong> application is running on ##application.wheels.serverName## with ##application.wheels.dataSourceName## (##get("environment")##).</p>' & nl &
nl &
'<h2>Next steps</h2>' & nl &
'<ul>' & nl &
tab & '<li><code>wheels g scaffold Post title content:text</code> &mdash; generate a model, controller, and views</li>' & nl &
tab & '<li><code>wheels migrate latest</code> &mdash; build the database schema</li>' & nl &
tab & '<li><code>wheels test</code> &mdash; run the test suite</li>' & nl &
'</ul>' & nl &
'<p><small>This page lives at <code>app/views/main/index.cfm</code>; routing is in <code>config/routes.cfm</code>.</small></p>' & nl &
'</cfoutput>' & nl
)
);
printCreated(appName & "/app/views/main/index.cfm");

Expand Down
25 changes: 24 additions & 1 deletion cli/tests/specs/e2e/ProjectScaffoldTest.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,16 @@ component extends="testbox.system.BaseSpec" {

var content = fileRead(path);
expect(content).toInclude("Welcome to testapp");
// Runtime expressions must survive generation as single-hash
// CFML (## -> # in the fileWrite string), not be evaluated at
// scaffold time. Locks in the escaping shared with Module.cfc.
expect(content).toInclude('##get("version")##');
expect(content).toInclude('##application.wheels.serverName##');
expect(content).toInclude("<cfoutput>");
expect(content).toInclude("Next steps");
expect(content).toInclude("wheels g scaffold");
expect(content).toInclude("wheels migrate latest");
expect(content).toInclude("wheels test");
});

it("generates base Controller.cfc in app/controllers/", function() {
Expand Down Expand Up @@ -325,7 +335,20 @@ component extends="testbox.system.BaseSpec" {

fileWrite(
arguments.targetDir & "/app/views/main/index.cfm",
'<h1>Welcome to ' & arguments.appName & '</h1>' & nl & '<p>Your Wheels application is running. Edit this file at app/views/main/index.cfm</p>' & nl
(
'<cfoutput>' & nl &
'<h1>Welcome to ' & arguments.appName & '</h1>' & nl &
'<p>Your <strong>Wheels ##get("version")##</strong> application is running on ##application.wheels.serverName## with ##application.wheels.dataSourceName## (##get("environment")##).</p>' & nl &
nl &
'<h2>Next steps</h2>' & nl &
'<ul>' & nl &
tab & '<li><code>wheels g scaffold Post title content:text</code> &mdash; generate a model, controller, and views</li>' & nl &
tab & '<li><code>wheels migrate latest</code> &mdash; build the database schema</li>' & nl &
tab & '<li><code>wheels test</code> &mdash; run the test suite</li>' & nl &
'</ul>' & nl &
'<p><small>This page lives at <code>app/views/main/index.cfm</code>; routing is in <code>config/routes.cfm</code>.</small></p>' & nl &
'</cfoutput>' & nl
)
);
}

Expand Down
3 changes: 2 additions & 1 deletion vendor/wheels/Public.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ component output="false" displayName="Internal GUI" extends="wheels.Global" {
/**
* Defense-in-depth: unless the current environment is `development`,
* short-circuit the handler with a 404 response before any view is
* included. Called as the first statement of every non-`index` handler in
* included. Called as the first statement of every handler in
* this component.
*/
public void function $blockInProduction() {
Expand Down Expand Up @@ -356,6 +356,7 @@ component output="false" displayName="Internal GUI" extends="wheels.Global" {
This is just a proof of concept
*/
function index() {
$blockInProduction();
include "/wheels/public/views/congratulations.cfm";
return "";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ component extends="wheels.WheelsTest" {
"ai",
"guideImage",
"assets",
"mcp"
"mcp",
"index"
];

for (var handler in gatedHandlers) {
Expand All @@ -170,17 +171,14 @@ component extends="wheels.WheelsTest" {
})(handler);
}

it("index() is NOT gated (congratulations page stays discoverable)", () => {
// The issue explicitly says leave index() reachable in dev/testing.
// In production enablePublicComponent=false already hides it at
// the dispatch layer, so no per-handler block is needed.
var pattern = "function\s+index\s*\([^)]*\)\s*\{\s*\$blockInProduction\s*\(\s*\)\s*;";
var matched = REFindNoCase(pattern, source) > 0;
expect(matched).toBeFalse(
"index() should not call $blockInProduction() — it's the congratulations "
& "page and the issue (##2233) explicitly keeps it discoverable."
);
});
// index() (the congratulations/welcome page at the /wheels namespace
// root) is now gated like every other handler above. #2233 originally
// left it ungated so the welcome page stayed reachable in dev/testing,
// relying on enablePublicComponent=false to hide /wheels in production.
// Reversed because the redesigned page surfaces version/engine/db/
// environment (#2098/#2272), the same class of detail the gated handlers
// protect, so it now defense-in-depth gates itself too. Development still
// renders (the gate is an allowlist: only "development" passes).

});

Expand Down
Loading