#181

Add Laravel security matchers and tighten Laravel detection

Openpushpak1300wants to merge intofrom

What changed

The scanner gets seven Laravel matchers for mass assignment, raw SQL, Blade XSS, missing authorization, unsafe sinks, config exposure, and Livewire/Nova. Laravel detection now matches the Rails and Django detectors: laravel/framework in composer.json or in the runtime packages of composer.lock, or an artisan file.

Why

Before this change, Laravel apps got one noisy route matcher. The generic XSS, redirect, SSRF, and deserialization matchers read only JS and TS files, so PHP sinks got no candidates.

The old detection tagged any repo with a laravel/* dependency or a routes/web.php file. Libraries that use laravel/pint or laravel/prompts, and Slim apps, got the Laravel matchers.

Verification

  • pnpm test passes (scanner and processor suites pass. cli/tarball.test.ts times out on main too, and core/shutdown.test.ts is flaky under load.)
  • pnpm lint passes
  • pnpm knip passes (it fails on packages/website on main too. This change adds no knip findings.)
  • If this adds a matcher: ran it against at least one real repo and confirmed the candidate count is sane (laravel.io: 56 matches in 50 of 417 PHP files. The new matchers gave 27 candidates, and the agent reported findings for 15 of their 24 files.)

Notes for reviewer

laravel-missing-authorization is noisy. It reads one file, so it can't see route-group middleware or a FormRequest::authorize() in another file. Each candidate is a lead for the agent to verify.

The detector adds livewire and nova tags. laravel-livewire-filament runs only when one of them is present.

laravel-config-exposure doesn't flag committed .env secrets, because env-exposure already does. Its slug is in SECRET_SLUGS, so committed data drops its snippets.

php-laravel-route drops its $request->all() and *Raw labels. laravel-mass-assignment and laravel-sql-raw now report those cases with input-aware patterns.

The Laravel prompt highlights and slug notes now cover the new matchers.

NOTE

A Laravel package repo isn't tagged laravel when it requires illuminate/* but not laravel/framework and has no artisan file. This also applies when its composer.lock lists laravel/framework only under packages-dev, for example through orchestra/testbench.

Activity

7
vercel[bot]1mo ago

@pushpak1300 is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

vercel-security-reviewer[bot]1mo ago
auto-maintain[bot]1mo ago

🤖 auto-maintain review

Automated, advisory triage for @pushpak1300's PR. Facts below are read from the GitHub API.

CheckResult
Author's merged PRs (this repo)0 — ⚠️ first-time contributor
Account established✅ (age 3316d · 456 followers · 74 public repos)
Commits signed/verified✅ 4/4

Review panel: 🟡 medium highest severity

deepsec maintainer code review: 🟡 medium

The Laravel coverage is useful, but authorization false negatives and stale persisted candidates should be fixed before merge.

  • packages/scanner/src/matchers/laravel-utils.ts:14 — Any custom `*Request` type-hint is treated as authorization, but Laravel FormRequests commonly return `true` from `authorize()` and provide validation only. This suppresses missing-authorization candidates for unprotected mutating actions; inspect the FormRequest or keep the action as a lead.
  • packages/scanner/src/matchers/laravel-utils.ts:24 — Class-level auth detection ignores middleware `only`/`except` scoping. For example, `middleware('auth')->except('destroy')` causes every action—including the explicitly unprotected destroy action—to be skipped.
  • packages/scanner/src/matchers/php-laravel-route.ts:34 — Removing the old `$request->all()` and raw-SQL patterns does not remove their persisted `php-laravel-route` candidates: scans merge into existing records without pruning. Existing projects therefore retain the noisy candidates indefinitely and also gain the replacement matcher candidates; this needs a migration, pruning step, or new slug.

General code review: 🟡 medium

The new matchers have authorization false negatives and a Laravel gating bypass that should be fixed before merge.

  • packages/scanner/src/matchers/laravel-utils.ts:24 — The matcher treats `auth` middleware and any `*Request` type hint as authorization. Authentication or a validation-only FormRequest does not enforce resource permissions, so authenticated IDOR-prone actions are silently skipped; scoped `except` middleware is also ignored.
  • packages/scanner/src/matchers/php-laravel-route.ts:19 — The sentinel regex accepts `"laravel/framework"` anywhere in composer.json. Because tech and sentinel gates are a union, `conflict`, `replace`, or `suggest` entries activate php-laravel-route in non-Laravel repositories, bypassing the tightened detector.

Scanner and matcher quality: 🟡 medium

The Laravel matcher suite has actionable authorization false negatives and an overbroad CORS rule that should be corrected before merge.

  • packages/scanner/src/matchers/laravel-utils.ts:36 — Any class-level auth middleware suppresses all action findings, ignoring `only()`/`except()` scope; e.g. auth limited to `index` hides an unprotected `destroy` action.
  • packages/scanner/src/matchers/laravel-livewire-filament.ts:66 — A single `can*` or `authorizedTo*` method suppresses the entire Resource candidate, so overriding only view authorization can hide unrestricted create/edit/delete operations when no policy exists.
  • packages/scanner/src/matchers/laravel-config-exposure.ts:82 — CORS settings are matched independently: wildcard origins are flagged even when credentials are disabled, and `supports_credentials => true` is flagged with a strict origin allowlist. Correlate both settings to avoid systematic false positives.

Sandbox and credential boundaries: 🟡 medium

Credential handling is sound (`laravel-config-exposure` is correctly added to SECRET_SLUGS so `.env`/config snippets are scrubbed before commit), but the new `$except` detection introduces a verified exponential-backtracking regex that an untrusted repo can use to hang the scanner, and the Laravel detector's require-dev handling contradicts its own runtime-only intent.

  • packages/scanner/src/matchers/laravel-config-exposure.ts:51 — Catastrophic backtracking: `/$except\s*=\s*[\s*(?://.\s)['"]/` has a nested-quantifier ambiguity (`.` then `\s*`, repeated) and is `.test()`ed against `lines.slice(i).join("\n")` — the entire remainder of the file rather than the array block. A `laravel`-tagged repo containing a file under `app/Http/Middleware/`, `config/`, `routes/`, `bootstrap/app.php`, or `app/Providers/ServiceProvider.php` with `$except = [` followed by ~15+ `//` comment lines that carry trailing whitespace and no quoted entry drives exponential backtracking (measured: 10 lines = 0.12s, 14 lines = 7.3s, ~4x per added line). The scanner has no per-matcher or per-file timeout, so attacker-authored repository content hangs the whole scan. Anchor the search to a bounded window after the `$except` line and drop the nested quantifier (e.g. `(?:\s//[^\n])`).
  • packages/scanner/src/detect-tech.ts:128 — The lockfile path deliberately reads only `packages` (excluding `packages-dev`), but `keys` is still built from `require` and `require-dev` of composer.json, so `has("laravel/framework")` is true for a package repo that lists `laravel/framework` under `require-dev`. The stated tightening therefore doesn't hold for the composer.json path, and such repos still get all seven new Laravel matchers plus the Laravel prompt block. Same applies to the new `livewire`/`nova` tags. Either restrict the lookup to `require` or drop the `packages-dev` exclusion so both paths agree.

Durability and distributed state: 🟡 medium

The matcher logic is added, but persisted scanner state prevents the new Laravel coverage from reliably activating on resumed and diff-scoped runs.

  • packages/scanner/src/matchers/index.ts:399 — New matcher candidates are append-merged into persisted FileRecords without resetting `analyzed` records to `pending`. Existing projects therefore skip all seven new Laravel candidates during normal processing, while removed php-laravel-route candidates remain stale unless users force reinvestigation.
  • packages/scanner/src/detect-tech.ts:144 — Diff-scoped scans reuse persisted tech.json indefinitely, so these new laravel/livewire/nova tags are not recomputed when composer.lock changes or when an existing cache predates this release. The new matchers remain gated off for those runs.

Posted by auto-maintain. This automated code review is advisory; a human maintainer makes the call.