diff --git a/app/Http/Controllers/CartController.php b/app/Http/Controllers/CartController.php
index 548566a..7845e1f 100644
--- a/app/Http/Controllers/CartController.php
+++ b/app/Http/Controllers/CartController.php
@@ -94,7 +94,7 @@ class CartController extends Controller
$validated = $request->validate([
'model_name' => 'nullable|string|max:255',
'model_url' => 'nullable|url|max:1000',
- 'model_file' => 'nullable|file|max:51200', // 50MB
+ 'model_file' => 'nullable|file|extensions:stl,3mf,obj,step,stp,zip|max:51200', // 50MB
'size' => 'required|in:klein,medium,groot',
'material' => 'required|string',
'color' => 'required|string',
diff --git a/app/Http/Controllers/ShopController.php b/app/Http/Controllers/ShopController.php
index 98b1352..ddf7b46 100644
--- a/app/Http/Controllers/ShopController.php
+++ b/app/Http/Controllers/ShopController.php
@@ -106,7 +106,7 @@ class ShopController extends Controller
'preferred_material' => 'nullable|string|max:50',
'preferred_color' => 'nullable|string|max:50',
'dimensions' => 'nullable|string|max:100',
- 'model_file' => 'nullable|file|max:51200', // max 50MB (stl, step, obj, zip)
+ 'model_file' => 'nullable|file|extensions:stl,3mf,obj,step,stp,zip|max:51200', // max 50MB (stl, step, obj, zip)
]);
$filePath = null;
diff --git a/app/Http/Middleware/AddSecurityHeaders.php b/app/Http/Middleware/AddSecurityHeaders.php
new file mode 100644
index 0000000..e45fd0b
--- /dev/null
+++ b/app/Http/Middleware/AddSecurityHeaders.php
@@ -0,0 +1,27 @@
+headers->set('X-Frame-Options', 'SAMEORIGIN');
+ $response->headers->set('X-Content-Type-Options', 'nosniff');
+ $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
+ $response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
+
+ return $response;
+ }
+}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index 8d87bdb..6bb05bc 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -1,5 +1,6 @@
withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(at: '*');
$middleware->web(append: [
+ AddSecurityHeaders::class,
CheckWebshopOffline::class,
]);
$middleware->alias([
diff --git a/resources/views/shop/show.blade.php b/resources/views/shop/show.blade.php
index 2b4cd89..860e586 100644
--- a/resources/views/shop/show.blade.php
+++ b/resources/views/shop/show.blade.php
@@ -204,7 +204,7 @@
Over deze 3D print
- {!! nl2br($product->description) !!}
+ {!! nl2br(strip_tags($product->description, '
diff --git a/routes/web.php b/routes/web.php
index 69e34d1..2fbed05 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -20,8 +20,12 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;
-// Health & Diagnostics
+// Health & Diagnostics (Alleen toegankelijk in debug-modus of voor ingelogde admins)
Route::get('/health-debug', function () {
+ if (! config('app.debug') && ! (auth()->check() && auth()->user()->isAdmin())) {
+ abort(404);
+ }
+
$dbPath = config('database.connections.sqlite.database');
$diagnostics = [
@@ -78,23 +82,23 @@ Route::get('/winkelwagen/aantal', [CartController::class, 'count'])->name('cart.
// Afrekenen & Bevestiging
Route::get('/afrekenen', [CheckoutController::class, 'index'])->name('checkout.index');
-Route::post('/afrekenen', [CheckoutController::class, 'store'])->name('checkout.store');
+Route::post('/afrekenen', [CheckoutController::class, 'store'])->name('checkout.store')->middleware('throttle:10,1');
Route::get('/bestelling-ontvangen/{orderNumber}', [CheckoutController::class, 'success'])->name('checkout.success');
// Authenticatie & Klantenportaal
Route::middleware('guest')->group(function () {
Route::get('/inloggen', [LoginController::class, 'showLoginForm'])->name('login');
- Route::post('/inloggen', [LoginController::class, 'login'])->name('login.submit');
+ Route::post('/inloggen', [LoginController::class, 'login'])->name('login.submit')->middleware('throttle:5,1');
Route::redirect('/login', '/inloggen');
Route::get('/registreren', [RegisterController::class, 'showRegistrationForm'])->name('register');
- Route::post('/registreren', [RegisterController::class, 'register'])->name('register.submit');
+ Route::post('/registreren', [RegisterController::class, 'register'])->name('register.submit')->middleware('throttle:6,1');
Route::redirect('/register', '/registreren');
Route::get('/wachtwoord-vergeten', [ForgotPasswordController::class, 'showLinkRequestForm'])->name('password.request');
- Route::post('/wachtwoord-vergeten', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email');
+ Route::post('/wachtwoord-vergeten', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email')->middleware('throttle:3,1');
Route::get('/wachtwoord-resetten/{token}', [ForgotPasswordController::class, 'showResetForm'])->name('password.reset');
- Route::post('/wachtwoord-resetten', [ForgotPasswordController::class, 'reset'])->name('password.update');
+ Route::post('/wachtwoord-resetten', [ForgotPasswordController::class, 'reset'])->name('password.update')->middleware('throttle:5,1');
});
Route::middleware('auth')->group(function () {
diff --git a/tests/Feature/AdminContentTest.php b/tests/Feature/AdminContentTest.php
index e4d4b0e..dd96ed9 100644
--- a/tests/Feature/AdminContentTest.php
+++ b/tests/Feature/AdminContentTest.php
@@ -303,4 +303,12 @@ class AdminContentTest extends TestCase
$guestOnlineResponse = $this->get(route('home'));
$guestOnlineResponse->assertStatus(200);
}
+
+ public function test_site_sends_modern_security_headers(): void
+ {
+ $response = $this->get('/');
+ $response->assertHeader('X-Frame-Options', 'SAMEORIGIN');
+ $response->assertHeader('X-Content-Type-Options', 'nosniff');
+ $response->assertHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
+ }
}