Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
10 changes: 9 additions & 1 deletion routes/buildora.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@
use Ginkelsoft\Buildora\Http\Controllers\TwoFactorController;
use Ginkelsoft\Buildora\Http\Controllers\BuildoraSettingsController;

Route::prefix(config('buildora.route_prefix', 'buildora'))
// De 'web'-middlewaregroep (sessies, CSRF-verificatie, cookie-encryptie)
// wordt hier hard-coded toegepast, los van config('buildora.middleware').
// Zo blijft CSRF-bescherming voor alle datatable- en bulk-action-routes
// (o.a. store/update/destroy en de acties die daarnaar posten) gegarandeerd
// actief, ook als een consumerende applicatie de gepubliceerde config
// aanpast en daarbij per ongeluk 'web' uit de middleware-lijst verwijdert.
// Zie issue #124.
Route::middleware('web')
->prefix(config('buildora.route_prefix', 'buildora'))
->middleware(config('buildora.middleware', ['web', 'buildora.auth', 'buildora.ensure-user-resource']))
->group(function () {

Expand Down
77 changes: 77 additions & 0 deletions tests/Feature/CsrfProtectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Ginkelsoft\Buildora\Tests\Feature;

use Ginkelsoft\Buildora\Tests\TestCase;

/**
* Regressietest voor issue #124: datatable- en bulk-action-routes moeten
* binnen de 'web'-middlewaregroep vallen, zodat CSRF-verificatie actief is.
*
* Laravel schakelt CSRF-verificatie automatisch uit tijdens unit tests
* (VerifyCsrfToken::runningUnitTests()), dus we forceren hier bewust een
* niet-testing omgeving om de echte middleware-pijplijn te doorlopen zoals
* die ook in productie draait.
*
* Een POST/PUT/DELETE zonder geldig CSRF-token op een Buildora-route moet
* resulteren in HTTP 419 (TokenMismatchException). Krijgt de aanvraag
* i.p.v. daarvan een 200/302 (bijv. een redirect naar de loginpagina),
* dan betekent dat dat de actie zonder CSRF-token is doorgelaten tot in
* de applicatielogica: een CSRF-kwetsbaarheid.
*/
class CsrfProtectionTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();

// Forceer echte CSRF-afdwinging (zie class-docblock hierboven).
$this->app['env'] = 'local';
}

public function test_post_to_switch_locale_without_csrf_token_is_rejected_with_419(): void
{
$response = $this->post('/buildora/switch-locale', [
'locale' => 'nl',
]);

$response->assertStatus(419);
}

public function test_post_to_resource_store_without_csrf_token_is_rejected_with_419(): void
{
// De dynamische resource-routes (store/update/destroy), waar ook
// bulk-actions naartoe posten, zitten genest in dezelfde groep en
// moeten dezelfde CSRF-bescherming erven.
$response = $this->post('/buildora/resource/users', [
'name' => 'Attacker',
]);

$response->assertStatus(419);
}

public function test_delete_to_resource_destroy_without_csrf_token_is_rejected_with_419(): void
{
// DELETE wordt door bulk-delete-acties gebruikt.
$response = $this->delete('/buildora/resource/users/1');

$response->assertStatus(419);
}

public function test_switch_locale_route_carries_the_web_middleware_group(): void
{
// Directe assertie op de route-registratie: bewijst dat de fix
// (of de bestaande, correcte registratie) niet per ongeluk wordt
// teruggedraaid, onafhankelijk van omgevingsdetectie-trucs hierboven.
$route = \Illuminate\Support\Facades\Route::getRoutes()->getByName('buildora.locale.switch');

$this->assertContains('web', $route->gatherMiddleware());
}

public function test_resource_store_route_carries_the_web_middleware_group(): void
{
$route = \Illuminate\Support\Facades\Route::getRoutes()->getByName('buildora.store');

$this->assertContains('web', $route->gatherMiddleware());
}
}
48 changes: 48 additions & 0 deletions tests/Feature/CsrfProtectionWithoutWebInConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Ginkelsoft\Buildora\Tests\Feature;

use Ginkelsoft\Buildora\Tests\TestCase;

/**
* Regressietest voor issue #124 (hardening): CSRF-bescherming mag niet
* afhankelijk zijn van een correct ingevulde config('buildora.middleware').
*
* Simuleert een consumerende applicatie die het gepubliceerde
* config/buildora.php aanpast en daarbij per ongeluk 'web' uit de
* middleware-lijst verwijdert. Ook dan moet CSRF-verificatie actief
* blijven, omdat de 'web'-groep hard-coded om de routes heen staat
* (zie routes/buildora.php). Zonder deze hardcodering zou dit scenario
* de datatable- en bulk-action-routes buiten CSRF-verificatie laten
* vallen.
*/
class CsrfProtectionWithoutWebInConfigTest extends TestCase
{
protected function getEnvironmentSetUp($app): void
{
parent::getEnvironmentSetUp($app);

$app['config']->set('buildora.middleware', [
'buildora.auth',
'buildora.ensure-user-resource',
]);
}

protected function setUp(): void
{
parent::setUp();

// Forceer echte CSRF-afdwinging; Laravel schakelt dit anders
// automatisch uit tijdens unit tests.
$this->app['env'] = 'local';
}

public function test_post_without_csrf_token_is_still_rejected_with_419_even_without_web_in_config(): void
{
$response = $this->post('/buildora/switch-locale', [
'locale' => 'nl',
]);

$response->assertStatus(419);
}
}
3 changes: 3 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ protected function getPackageProviders($app): array

protected function getEnvironmentSetUp($app): void
{
// App key is nodig voor sessie-encryptie en CSRF-tokens.
$app['config']->set('app.key', 'base64:' . base64_encode(random_bytes(32)));

// Setup default database to use sqlite :memory:
$app['config']->set('database.default', 'testbench');
$app['config']->set('database.connections.testbench', [
Expand Down
Loading