Pulse Security Analyzer
| Analyzer ID | Category | Severity | Time To Fix |
|---|---|---|---|
pulse-security | 🛡️ Security | High | 10 minutes |
What This Checks
Validates Laravel Pulse dashboard security. Checks for:
viewPulsegate defined in a service provider (AppServiceProvider, AuthServiceProvider, or PulseServiceProvider)- Gate callback is not trivially permissive (
return true,fn() => true, auth-only,?? truefallback) - Data retention (
keepconfig) is 7 days or less - Data trimming lottery is not disabled (
lotterynot set to[0, …])
Why It Matters
- Public Dashboard - Without the
viewPulsegate, the/pulseroute is accessible to any authenticated user in non-local environments, exposing slow queries, job timings, request data, and exception counts - Weak Authorization - A gate that returns
trueor only checksauth()->check()grants the full dashboard to every logged-in user, not just administrators - Sensitive Query Data - Pulse records slow query text and timing; this information can reveal table names, column names, and data patterns useful to attackers
- Database Growth - Without data trimming, Pulse's storage tables grow unbounded and can exhaust disk space in high-traffic applications
- Data Minimisation - Retaining monitoring data longer than necessary increases the blast radius of a database breach
How to Fix
Quick Fix (5 minutes)
Define the viewPulse gate in AppServiceProvider::boot():
Before (❌):
php
// app/Providers/AppServiceProvider.php
public function boot(): void
{
// No viewPulse gate — dashboard open to all authenticated users
}After (✅):
php
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Gate;
public function boot(): void
{
Gate::define('viewPulse', function (User $user) {
return $user->isAdmin();
});
}Proper Fix (10 minutes)
1. Fix a permissive gate:
Before (❌):
php
Gate::define('viewPulse', function ($user) {
return true; // Grants access to everyone
});After (✅):
php
Gate::define('viewPulse', function (User $user) {
return in_array($user->email, [
'admin@example.com',
'devops@example.com',
], true);
});2. Configure data retention and trimming:
Before (❌):
php
// config/pulse.php
'trim' => [
'lottery' => [0, 100], // Trimming disabled
'keep' => CarbonInterval::days(30), // Excessive retention
],After (✅):
php
// config/pulse.php
'trim' => [
'lottery' => [1, 1000], // Trim on ~0.1% of requests
'keep' => CarbonInterval::days(7),
],References
- Laravel Pulse Documentation
- Laravel Pulse Authorization
- OWASP Access Control Cheat Sheet
- CWE-284: Improper Access Control
- CWE-359: Exposure of Private Personal Information
Related Analyzers
- Telescope Security - Validates Telescope debug tool security
- Horizon Security - Validates Horizon dashboard security
- Debug Mode - Validates debug mode configuration