From d544a97c7f4f00115175d3a63f9908bda173afb0 Mon Sep 17 00:00:00 2001 From: Ambrose Casanova <279373485+ambrose5773@users.noreply.github.com> Date: Mon, 5 Oct 2026 07:05:52 -0600 Subject: [PATCH 1/3] feat: enable view path restriction and disable method override in bootstrap New projects get flight.allow_method_override off and View::$restrictToPath on when core supports it. Twig still uses its own loader root under app/views. --- SECURITY.md | 2 ++ app/config/bootstrap.php | 8 ++++++++ tests/Unit/BootstrapHardeningTest.php | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 tests/Unit/BootstrapHardeningTest.php diff --git a/SECURITY.md b/SECURITY.md index 54a2d2e..2da0bc8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -60,6 +60,8 @@ When adding pages with third-party scripts, styles, or iframes, **update CSP del - Read input via **`$app->request()`** (query, data, cookies) — not `$_GET` / `$_POST` / `$_COOKIE` in app code. - **Twig auto-escapes** by default — do not use `|raw` / unescaped output unless the content is trusted and you understand the risk. +- Bootstrap sets **`$app->view()->restrictToPath = true`** so Flight's native `View` cannot include templates outside `flight.views.path`. Twig's `FilesystemLoader` already roots at `app/views/`. Do not turn `restrictToPath` off without a documented reason. +- Bootstrap also sets **`flight.allow_method_override` to `false`**. Only re-enable it if you need HTML forms to spoof `PUT`/`DELETE` via `_method`. - For JSON APIs, do not reflect unsanitized input into HTML elsewhere without escaping. - Validate and authorize **server-side**; never trust client-only checks. diff --git a/app/config/bootstrap.php b/app/config/bootstrap.php index 809c8f5..0f724ba 100644 --- a/app/config/bootstrap.php +++ b/app/config/bootstrap.php @@ -65,6 +65,14 @@ $app->set('flight.views.path', __DIR__ . $ds . '..' . $ds . 'views'); $app->set('flight.views.extension', '.twig'); $app->set('flight.content_length', false); +// Harden: do not allow X-HTTP-Method-Override / _method spoofing unless you need it +$app->set('flight.allow_method_override', false); +// Harden: keep native View templates inside flight.views.path (Twig has its own loader root). +// property_exists keeps this safe until flightphp/core ships View::$restrictToPath. +$view = $app->view(); +if (property_exists($view, 'restrictToPath')) { + $view->restrictToPath = true; +} // CSP nonce per request (used by SecurityHeadersMiddleware + Twig globals) $nonce = bin2hex(random_bytes(16)); diff --git a/tests/Unit/BootstrapHardeningTest.php b/tests/Unit/BootstrapHardeningTest.php new file mode 100644 index 0000000..94808b8 --- /dev/null +++ b/tests/Unit/BootstrapHardeningTest.php @@ -0,0 +1,23 @@ +assertNotFalse($bootstrap); + $this->assertStringContainsString("set('flight.allow_method_override', false)", $bootstrap); + $this->assertStringContainsString("property_exists($view, 'restrictToPath')", $bootstrap); + $this->assertStringContainsString('restrictToPath = true', $bootstrap); + } +} From 6ad3c69f10b6ac60659715defee485f0113d9593 Mon Sep 17 00:00:00 2001 From: Ambrose Casanova <279373485+ambrose5773@users.noreply.github.com> Date: Mon, 5 Oct 2026 07:05:59 -0600 Subject: [PATCH 2/3] test: escape dollar in BootstrapHardeningTest assertion --- tests/Unit/BootstrapHardeningTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/BootstrapHardeningTest.php b/tests/Unit/BootstrapHardeningTest.php index 94808b8..06fbeb8 100644 --- a/tests/Unit/BootstrapHardeningTest.php +++ b/tests/Unit/BootstrapHardeningTest.php @@ -17,7 +17,7 @@ public function testBootstrapEnablesViewPathRestrictionAndDisablesMethodOverride $bootstrap = file_get_contents(dirname(__DIR__, 2) . '/app/config/bootstrap.php'); $this->assertNotFalse($bootstrap); $this->assertStringContainsString("set('flight.allow_method_override', false)", $bootstrap); - $this->assertStringContainsString("property_exists($view, 'restrictToPath')", $bootstrap); + $this->assertStringContainsString("property_exists(\$view, 'restrictToPath')", $bootstrap); $this->assertStringContainsString('restrictToPath = true', $bootstrap); } } From 297b9dca08161087c67195a9d20317405722af2f Mon Sep 17 00:00:00 2001 From: Ambrose Casanova <279373485+ambrose5773@users.noreply.github.com> Date: Mon, 5 Oct 2026 07:18:49 -0600 Subject: [PATCH 3/3] feat: set flight.views.restrict_to_path in bootstrap Use Flight::set for view path containment, same as the other hardening switches. Harmless on older core that ignores the key. --- SECURITY.md | 2 +- app/config/bootstrap.php | 7 ++----- tests/Unit/BootstrapHardeningTest.php | 4 +--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 2da0bc8..dd2eeab 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -60,7 +60,7 @@ When adding pages with third-party scripts, styles, or iframes, **update CSP del - Read input via **`$app->request()`** (query, data, cookies) — not `$_GET` / `$_POST` / `$_COOKIE` in app code. - **Twig auto-escapes** by default — do not use `|raw` / unescaped output unless the content is trusted and you understand the risk. -- Bootstrap sets **`$app->view()->restrictToPath = true`** so Flight's native `View` cannot include templates outside `flight.views.path`. Twig's `FilesystemLoader` already roots at `app/views/`. Do not turn `restrictToPath` off without a documented reason. +- Bootstrap sets **`flight.views.restrict_to_path` to `true`** so Flight's native `View` cannot include templates outside `flight.views.path`. Twig's `FilesystemLoader` already roots at `app/views/`. Do not turn this off without a documented reason. - Bootstrap also sets **`flight.allow_method_override` to `false`**. Only re-enable it if you need HTML forms to spoof `PUT`/`DELETE` via `_method`. - For JSON APIs, do not reflect unsanitized input into HTML elsewhere without escaping. - Validate and authorize **server-side**; never trust client-only checks. diff --git a/app/config/bootstrap.php b/app/config/bootstrap.php index 0f724ba..ec9c58f 100644 --- a/app/config/bootstrap.php +++ b/app/config/bootstrap.php @@ -68,11 +68,8 @@ // Harden: do not allow X-HTTP-Method-Override / _method spoofing unless you need it $app->set('flight.allow_method_override', false); // Harden: keep native View templates inside flight.views.path (Twig has its own loader root). -// property_exists keeps this safe until flightphp/core ships View::$restrictToPath. -$view = $app->view(); -if (property_exists($view, 'restrictToPath')) { - $view->restrictToPath = true; -} +// Harmless on older core that ignores the key; applied when flightphp/core supports it. +$app->set('flight.views.restrict_to_path', true); // CSP nonce per request (used by SecurityHeadersMiddleware + Twig globals) $nonce = bin2hex(random_bytes(16)); diff --git a/tests/Unit/BootstrapHardeningTest.php b/tests/Unit/BootstrapHardeningTest.php index 06fbeb8..110fc25 100644 --- a/tests/Unit/BootstrapHardeningTest.php +++ b/tests/Unit/BootstrapHardeningTest.php @@ -8,7 +8,6 @@ /** * Guards the security switches we expect in bootstrap for new projects. - * Runtime View::$restrictToPath requires flightphp/core with that property. */ class BootstrapHardeningTest extends TestCase { @@ -17,7 +16,6 @@ public function testBootstrapEnablesViewPathRestrictionAndDisablesMethodOverride $bootstrap = file_get_contents(dirname(__DIR__, 2) . '/app/config/bootstrap.php'); $this->assertNotFalse($bootstrap); $this->assertStringContainsString("set('flight.allow_method_override', false)", $bootstrap); - $this->assertStringContainsString("property_exists(\$view, 'restrictToPath')", $bootstrap); - $this->assertStringContainsString('restrictToPath = true', $bootstrap); + $this->assertStringContainsString("set('flight.views.restrict_to_path', true)", $bootstrap); } }