salvatorecervone / laravel-pentest
Automated In-App Penetration Testing and DAST Security Audit Suite for Laravel
Package info
github.com/SalvatoreCervone/laravel-pentest
pkg:composer/salvatorecervone/laravel-pentest
Requires
- php: ^8.2
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/routing: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Automated In-App Penetration Testing and DAST Security Audit Suite for Laravel.
laravel-pentest turns your Laravel CLI and test environment into an automated, in-app offensive security tester. It scans and simulates real-world attack vectors against your routes, Eloquent models, controllers, database connections, and configurations to detect critical security gaps before bad actors do:
- BOLA / IDOR vulnerabilities (missing Policy & Gate authorizations on resource models)
- Mass Assignment flaws (
$guarded = [], risky$fillablecolumns, unfiltered$request->all()) - Unprotected Admin Routes (sensitive endpoints missing authentication middleware)
- Missing Rate Limiters (brute-force exposure on login, 2FA, and password-reset endpoints)
- Insecure HTTP Security Headers (missing CSP, HSTS, X-Frame-Options, X-Content-Type-Options)
- Active Debug & Secret Leaks (
APP_DEBUG=truein production, exposed.envand.gitfiles) - Database Hardening & Least Privilege (weak/empty passwords, root/superuser connection, missing TLS/SSL on remote DBs, exposed SQLite)
- Sensitive Data & PII Exposure (unencrypted PII columns without
casts = ['...' => 'encrypted'], secrets leaked outside$hidden) - Cryptographic & Key Integrity (missing/weak
APP_KEY, low bcrypt work factors, insecure.envfilesystem permissions) - SQL Injection in Raw Queries (string concatenations in
whereRaw(),havingRaw(),orderByRaw(), andDB::raw()) - Session & Cookie Hardening (missing
secure, disabledhttp_only, insecuresame_siteflags) - CORS Misconfiguration (wildcard origins with credentials, permissive regexes, sensitive exposed headers)
Table of Contents
- Features
- Requirements
- Installation
- Usage
- Audit Suites & Vulnerabilities Detected
- CI/CD Integration (GitHub Actions)
- Configuration
- Running Tests
- Security Vulnerabilities
- License
Requirements
- PHP:
^8.2 - Laravel Framework:
^10.0,^11.0, or^12.0
Installation
Install the package via Composer as a development dependency:
composer require --dev salvatorecervone/laravel-pentest
Publish the package configuration file (optional):
php artisan vendor:publish --tag="pentest-config"
Usage
Run All Penetration Tests
php artisan pentest:run
Output preview:
Initiating Laravel In-App Penetration Testing Suite (6 audits loaded)...
→ Running audit: route-exposure... [PASS]
→ Running audit: idor-bola... [FAIL: 1 findings]
→ Running audit: mass-assignment... [PASS]
→ Running audit: rate-limit... [PASS]
→ Running audit: security-headers... [PASS]
→ Running audit: debug-exposure... [PASS]
+------------------+--------+----------+------+--------+-----+----------+
| Audit Suite | Status | Critical | High | Medium | Low | Duration |
+------------------+--------+----------+------+--------+-----+----------+
| route-exposure | PASS | 0 | 0 | 0 | 0 | 1.45 ms |
| idor-bola | FAIL | 0 | 1 | 0 | 0 | 2.10 ms |
| mass-assignment | PASS | 0 | 0 | 0 | 0 | 3.20 ms |
| rate-limit | PASS | 0 | 0 | 0 | 0 | 0.85 ms |
| security-headers | PASS | 0 | 0 | 0 | 0 | 1.10 ms |
| debug-exposure | PASS | 0 | 0 | 0 | 0 | 0.40 ms |
+------------------+--------+----------+------+--------+-----+----------+
=== DETECTED VULNERABILITIES & REMEDIATION ===
[HIGH] [CWE-639] Missing authorization policy on [GET] /api/documents/{document} (/api/documents/{document})
The endpoint binds to a data model 'App\Models\Document' but does not enforce Policy or Gate authorization.
Remediation: Enforce a policy check: add '->middleware("can:view,document")' or call '$this->authorize("view", $document)'.
Run a Specific Audit Suite
Filter which audit suite to execute using the --suite option:
# Available options: route-exposure, idor-bola, mass-assignment, rate-limit, security-headers, debug-exposure, database-security, sensitive-data, crypto-security, csrf-protection, dependency-security
php artisan pentest:run --suite=csrf-protection
Baseline & False-Positive Suppression
When introducing laravel-pentest to an existing codebase with legacy debt, avoid breaking the CI build by generating a baseline file:
# Snapshot all current findings into .pentest-baseline.json
php artisan pentest:run --generate-baseline
Subsequent runs will automatically suppress known baseline issues, ensuring only new security regressions trigger alerts and fail the CI pipeline:
php artisan pentest:run --fail-on-vuln
Fail CI Pipeline on Vulnerabilities
Exit with status code 1 if any Critical or High vulnerability is identified:
php artisan pentest:run --fail-on-vuln
Or exit with status code 1 on any finding (including low or medium):
php artisan pentest:run --strict
Generate Standalone HTML Report
Export a beautifully formatted, responsive standalone HTML security report:
php artisan pentest:run --report=storage/reports/security-audit.html
Interactive Web Security Dashboard
Launch live security scans directly in your browser with real-time feedback and an interactive vulnerability explorer:
- Access URL:
http://localhost:8000/pentest(or your configured application domain) - Enabled by default in
localandtestingenvironments (configurable inconfig/pentest.php). - Zero external dependencies (pure vanilla CSS & lightweight JS).
- Allows developers to trigger full audits with a single click and review findings with remediation advice instantly.
Security Baseline & False Positive Suppressions
Prevent legacy findings from blocking ongoing CI workflows while ensuring zero new regressions:
# Snapshot existing vulnerabilities into a baseline file php artisan pentest:run --generate-baseline=pentest-baseline.json # Run subsequent scans ignoring existing baseline findings php artisan pentest:run --baseline=pentest-baseline.json --fail-on-vuln
You can also suppress specific false positives permanently in config/pentest.php:
'suppressions' => [ 'UNPROTECTED_SENSITIVE_ROUTE:/api/public-report', ],
Safe Database Probing (DatabaseSandbox)
When performing active security probes or simulating input mutation against models and database connections, laravel-pentest provides transactional rollback isolation:
use LaravelPentest\Support\DatabaseSandbox; DatabaseSandbox::executeInTransaction(function () { // Probing or test mutation runs here // Automatically rolled back in a finally block — zero persistence! });
Audit Suites & Vulnerabilities Detected
| Suite | Vulnerability ID | CWE | Default Severity | What It Tests |
|---|---|---|---|---|
route-exposure |
UNPROTECTED_SENSITIVE_ROUTE |
CWE-306 | CRITICAL / HIGH |
Scans registered routes for sensitive paths (admin/*, users/*, billing/*, reports/*) that lack authentication middleware. |
idor-bola |
POTENTIAL_IDOR_BOLA |
CWE-639 | HIGH |
Analyzes Route-Model binding endpoints to verify that Policies, Gates, or FormRequests protect user resources against horizontal privilege escalation. |
mass-assignment |
UNGUARDED_ELOQUENT_MODEL |
CWE-915 | CRITICAL |
Detects models declaring $guarded = [] without $fillable, leaving database columns open to arbitrary modifications. |
mass-assignment |
DANGEROUS_FILLABLE_ATTRIBUTES |
CWE-915 | HIGH |
Identifies privileged fields (is_admin, role_id, balance, permissions, is_superadmin) dangerously declared inside $fillable. |
mass-assignment |
UNFILTERED_REQUEST_MASS_ASSIGNMENT |
CWE-915 | HIGH |
Flags controller methods passing unfiltered $request->all() directly into Model::create() or $model->update(). |
csrf-protection |
MISSING_CSRF_PROTECTION |
CWE-352 | HIGH |
Verifies state-changing routes (POST, PUT, PATCH, DELETE) enforce CSRF token validation. |
rate-limit |
MISSING_RATE_LIMITING |
CWE-307 | HIGH |
Ensures that authentication, login, password recovery, and token endpoints enforce throttling (throttle:x,y) against brute force. |
security-headers |
MISSING_X_FRAME_OPTIONS |
CWE-1021 | MEDIUM |
Checks HTTP responses for clickjacking defenses (X-Frame-Options: SAMEORIGIN / DENY). |
security-headers |
MISSING_CSP |
CWE-79 | MEDIUM |
Checks for missing Content-Security-Policy (CSP) header. |
security-headers |
MISSING_HSTS |
CWE-319 | MEDIUM |
Asserts that Strict-Transport-Security (HSTS) is enabled in production. |
security-headers |
MISSING_X_CONTENT_TYPE_OPTIONS |
CWE-79 | LOW |
Verifies the X-Content-Type-Options: nosniff header to block MIME sniffing. |
debug-exposure |
APP_DEBUG_ENABLED_IN_PRODUCTION |
CWE-489 | CRITICAL |
Detects APP_DEBUG=true in production environments, exposing stack traces and database credentials. |
debug-exposure |
EXPOSED_SENSITIVE_FILE |
CWE-552 | CRITICAL / HIGH |
Checks whether sensitive files (.env, .git, storage/logs/laravel.log, .sql dumps) are placed in the public/ web root. |
database-security |
EMPTY_DATABASE_PASSWORD |
CWE-521 | CRITICAL |
Flags blank/missing passwords on network database connections. |
database-security |
WEAK_DATABASE_PASSWORD |
CWE-521 | CRITICAL |
Detects trivial/default passwords (root, admin, 123456, secret). |
database-security |
HIGHLY_PRIVILEGED_DATABASE_USER |
CWE-250 | HIGH |
Detects connecting as DBA/superuser (root, postgres, sa), violating Least Privilege. |
database-security |
UNENCRYPTED_REMOTE_DATABASE_CONNECTION |
CWE-319 | HIGH |
Verifies remote databases enforce TLS/SSL (sslmode=require / MYSQL_ATTR_SSL_CA). |
database-security |
EXPOSED_SQLITE_DATABASE |
CWE-552 | CRITICAL |
Flags SQLite databases stored inside the public/ web directory. |
sensitive-data |
UNENCRYPTED_SENSITIVE_ATTRIBUTE |
CWE-311 | HIGH |
Identifies PII columns (ssn, tax_number, credit_card, iban) missing casts = ['...' => 'encrypted']. |
sensitive-data |
SENSITIVE_ATTRIBUTE_NOT_HIDDEN |
CWE-200 | HIGH |
Detects secrets and auth tokens (password, two_factor_secret, api_token) missing from Eloquent $hidden. |
crypto-security |
MISSING_APP_KEY / PLACEHOLDER_APP_KEY |
CWE-321 | CRITICAL |
Asserts APP_KEY exists, is non-default, and matches cipher bit requirements. |
crypto-security |
WEAK_BCRYPT_ROUNDS |
CWE-916 | HIGH |
Validates password hashing work factors (bcrypt.rounds >= 10, recommended 12+). |
dependency-security |
VULNERABLE_COMPOSER_DEPENDENCY |
CWE-1395 | CRITICAL / HIGH |
Scans composer.lock for known advisories and CVEs in dependencies. |
dependency-security |
ABANDONED_OR_DEPRECATED_PACKAGE |
CWE-1104 | MEDIUM |
Warns about abandoned packages with known migration alternatives. |
cors-security |
CORS_WILDCARD_WITH_CREDENTIALS |
CWE-942 | CRITICAL |
Detects wildcard allowed origins (*) combined with supports_credentials => true. |
cors-security |
CORS_PERMISSIVE_ORIGIN_REGEX |
CWE-942 | HIGH |
Flags unanchored or overly permissive regular expressions in allowed_origins_patterns. |
cors-security |
CORS_SENSITIVE_EXPOSED_HEADER |
CWE-200 | MEDIUM |
Identifies authorization and session headers exposed to cross-origin callers. |
session-security |
SESSION_COOKIE_NOT_SECURE |
CWE-614 | HIGH |
Ensures session cookies enforce the secure flag in production/staging environments. |
session-security |
SESSION_COOKIE_NOT_HTTPONLY |
CWE-1004 | CRITICAL |
Asserts http_only => true to prevent session hijacking via JavaScript document.cookie. |
session-security |
SESSION_COOKIE_SAMESITE_INSECURE |
CWE-1275 | HIGH |
Validates same_site => 'lax' or 'strict' to prevent cross-site request leakage. |
session-security |
EXCESSIVE_SESSION_LIFETIME |
CWE-613 | LOW |
Warns if session expiration exceeds 30 days without re-authentication. |
sqli-safety |
SQL_INJECTION_RAW_QUERY |
CWE-89 | CRITICAL |
Scans controller methods for unparameterized string concatenations in whereRaw, havingRaw, orderByRaw, and DB::raw. |
Writing Tests with InteractsWithPentest
Integrate pentest assertions directly into your existing PHPUnit or Pest test suites:
namespace Tests\Feature; use App\Models\Customer; use App\Models\User; use LaravelPentest\Testing\InteractsWithPentest; use Tests\TestCase; class SecurityRegressionTest extends TestCase { use InteractsWithPentest; public function test_sensitive_routes_are_protected_from_guests(): void { $this->assertRouteProtectedFromGuest('/admin/dashboard'); $this->assertRouteProtectedFromGuest('/billing/invoices'); } public function test_auth_routes_have_rate_limiting(): void { $this->assertRouteHasRateLimiting('/login'); $this->assertRouteHasRateLimiting('/password/reset'); } public function test_models_have_encryption_and_hidden_attributes(): void { $this->assertModelIsGuarded(User::class); $this->assertModelHidesAttributes(User::class, ['password', 'two_factor_secret']); $this->assertModelEncryptsAttributes(Customer::class, ['ssn', 'credit_card']); } public function test_no_critical_pentest_vulnerabilities_exist(): void { $this->assertNoPentestVulnerabilities(); } }
CI/CD Integration (GitHub Actions)
Add this workflow to your repository (.github/workflows/security-pentest.yml) to automatically block pull requests containing new security regressions:
name: Security Pentest Audit on: push: branches: [ "main", "develop" ] pull_request: branches: [ "main" ] jobs: security-audit: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.3' extensions: mbstring, pdo_sqlite, curl - name: Install Dependencies run: composer install --prefer-dist --no-progress - name: Run In-App Penetration Tests run: php artisan pentest:run --fail-on-vuln --report=security-report.html - name: Upload Security Report Artifact uses: actions/upload-artifact@v4 if: always() with: name: security-audit-report path: security-report.html
Configuration
You can customize the scan behavior in config/pentest.php:
return [ // Patterns of routes to skip (e.g. dev debuggers, health routes) 'ignore_routes' => [ '_ignition/*', '_debugbar/*', 'telescope/*', 'horizon/*', 'pulse/*', 'up', 'sanctum/csrf-cookie', ], // Additional directories where Eloquent models should be discovered 'models_paths' => [ // app_path('Domain/Billing/Models'), ], // Minimum severity to fail CI command ('CRITICAL', 'HIGH', 'MEDIUM', 'LOW') 'fail_on_severity' => env('PENTEST_FAIL_SEVERITY', 'HIGH'), // Database Hardening & Security Audit 'database' => [ // Connections to audit. Null defaults to config('database.default') 'connections' => env('PENTEST_DB_CONNECTIONS', null), // Fallback/override credentials when not stored in .env // Useful for CI runners, Docker secrets, or AWS Secrets Manager 'credentials' => [ 'mysql' => [ 'username' => env('PENTEST_DB_USERNAME'), 'password' => env('PENTEST_DB_PASSWORD'), ], ], // Allow blank passwords if database uses socket or IAM authentication 'allow_empty_password' => env('PENTEST_DB_ALLOW_EMPTY_PASSWORD', false), // Custom weak password dictionary 'weak_passwords' => [ '', 'root', 'admin', 'password', '123456', 'secret', ], ], // Sensitive Data & PII Audit 'sensitive_data' => [ // Columns that should have 'encrypted' casts in Eloquent 'sensitive_attributes' => [ 'ssn', 'tax_number', 'fiscal_code', 'credit_card', 'iban', 'bank_account', 'passport', ], // Attributes that must be hidden from API JSON responses 'critical_hidden_attributes' => [ 'password', 'remember_token', 'two_factor_secret', 'api_token', ], ], // Cryptographic & Environment Audit 'crypto' => [ // Minimum bcrypt rounds (OWASP recommends >= 10, default 10) 'min_bcrypt_rounds' => 10, // Check if .env has world-readable filesystem permissions 'check_env_permissions' => true, ], ];
Running Tests
Run the test suite with PHPUnit:
composer test
Security Vulnerabilities
If you discover a security vulnerability within laravel-pentest, please open an issue or submit a pull request on GitHub.
License
The MIT License (MIT). Please see License File for more information.