Laravel security
Skill HamzaAlayed/laravel-claude-agents/skills/laravel-security
The Laravel security-review cookbook — STRIDE applied to Laravel primitives, the authn / authz / mass-assignment / upload / injection checklist, advisory lookup procedure, and the finding format. Use when threat-modeling a feature, reviewing a PR that touches auth / PII / billing / uploads / middleware, or triaging a suspected vulnerability. Findings only — fixes land with the owning builder after human awareness.From its SKILL.md
npx -y skills add HamzaAlayed/laravel-claude-agents --skill laravel-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 13 stars13 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.7 KB, as published. Nobody here has run it
Laravel Security Review
Verify before asserting. Trace the exploit path (route → middleware, incl. #[Middleware]/#[Authorize] controller attributes on L13 → FormRequest → Policy → query) before reporting — a grep over routes/middleware alone misses attribute-based coverage. Unverifiable → mark Suspected + the evidence needed. Never invent CVE IDs or CVSS scores — cite only advisories actually fetched.
STRIDE on Laravel primitives
| Threat | Laravel check |
|---|---|
| Spoofing | Guard per route group correct (config/auth.php); session fixation (Session::regenerate() on login); Sanctum SPA cookie domain matches frontend origin; password-reset tokens single-use + time-bound |
| Tampering | Mass assignment ($fillable deliberate, no $guarded = [] on user input); hidden-field trust; signed URLs (URL::signedRoute()) for out-of-band links; Livewire scalar props client-settable → #[Locked] on IDs used in authz |
| Repudiation | State-changing actions logged with actor + tenant; audit records append-only |
| Info disclosure | API Resources (never raw models — hidden columns leak); error pages / debug mode (APP_DEBUG prod), stack traces in responses, source maps; logs redact PII + secrets |
| DoS | Unbounded queries (no pagination), upload size/count limits, queue flooding, missing RateLimiter::for() on auth / OTP / expensive endpoints |
| Elevation | Policy on every state-changing route (can: middleware, FormRequest authorize(), Gate::authorize(), or L13 #[Authorize] attribute); policy registration includes #[UsePolicy] on models alongside auto-discovery; IDOR — route-model binding scoped (->scopeBindings() or tenant global scope); Gate::before not granting wholesale |
Document trust boundaries crossed + data sensitivity at each.
The checklist beyond STRIDE
- SQL / query: no
DB::rawwith concatenated input — bindings only;orderBy(request('sort'))is injection — allowlist the column. - XSS:
{!! !!}only with a written safety argument; user HTML → sanitize (mews/purifier or equivalent); JSON in Blade via@js/Js::from. - Uploads: validate
mimetypes:(server-side, not extension), size; store on non-public disk; never trust client filename (hashName()); images → strip EXIF where PII-relevant. - CSRF: web routes covered; deliberate exceptions (
preventRequestForgery(except:)inbootstrap/app.phpon L13 —validateCsrfTokensis the deprecated L11/12 alias, middleware class renamedVerifyCsrfToken→PreventRequestForgery) each justified. L13 verifies origin viaSec-Fetch-Sitefirst, token fallback second — HTTPS-only, so check reverse proxies/CDNs don't strip the header, and test exclusions referencePreventRequestForgery::class. - Secrets: never in repo,
.env.*committed, or logs.config()only —env()outsideconfig/*.phpbreaks under cache and hints at drift. Scan diffs:gitleaks detect. SuspectedAPP_KEYexposure → rotate viaAPP_PREVIOUS_KEYS(graceful re-encryption), never an ad-hoc key swap that bricks encrypted data and sessions. - Jobs / queues: PII-bearing jobs
ShouldBeEncrypted; failed-job payloads (failed_jobs.payload) hold serialized PII — retention policy exists. - Headers / CORS:
config/cors.phporigins explicit (no*with credentials) — file absent means unpublished defaults apply;php artisan config:publish corsbefore asserting origins. CookiesSecure,HttpOnly,SameSite.
Advisory lookup
composer audit(uses the packagist advisory DB) — zero known-vulnerable deps to pass.- For a specific package/version: GitHub Security Advisories + the package's own security policy. Fetch, don't recall.
- Framework CVEs: laravel/framework security releases — check the project's exact major is still patched.
Finding format
One finding = severity (CVSS 4.0 estimate — 3.1 when that's all the advisory carries — or Critical/High/Med/Low + why) + OWASP Top 10:2025 tag + CWE ID · exploit path (concrete route/file:line chain) · blast radius (data / tenants affected) · remediation naming the Laravel primitive ("add OrderPolicy::update + can:update,order on the route", not "add authorization"). Sort by severity. Suspected section separate from Confirmed.