AI assistants are very good at producing Laravel that looks right and runs in a demo. They are also very good at reproducing the insecure patterns they were trained on. After auditing a steady stream of Cursor-, Copilot- and Claude-built apps over the past two years, the same seven flaws account for the large majority of critical findings. This article walks through each one: what it looks like, why assistants keep writing it, and the exact fix.
If you want the one-sentence summary: AI-generated Laravel fails on authorization and input boundaries, not on exotic exploits. Everything below is fixable in an afternoon once you know where to look.
$request->all()The classic, and still the most common critical finding. The controller passes the entire request body straight into Eloquent:
If a sensitive column is fillable — and assistants love setting protected $guarded = []; on models to silence MassAssignmentException — anyone can add is_admin=1 or role=owner to the request and write it. Your Blade form not having that field is irrelevant; the attacker is not using your form.
The fix is to validate and pass only the validated data:
Keep an allow-list in $fillable as the second layer, and enable Model::shouldBeStrict() in development so silently discarded attributes throw instead of hiding bugs. We cover the whole topic in plain language in Mass assignment, explained for non-security folks.
Assistants reliably add authentication — the route sits behind auth middleware — and reliably forget authorization: checking that this particular user may touch this particular record.
This is an insecure direct object reference, and it is the single most damaging class of bug we find: it leaks customer data, and on write routes it lets users edit or delete records they do not own. Fix it with a policy and an explicit check:
For nested resources, scope route model bindings to the parent (->scopeBindings()) so /teams/1/invoices/99 404s when invoice 99 belongs to another team. The test is mechanical: open two accounts, swap IDs in every URL and API call, and watch what comes back.
The moment a query gets slightly complex — a dynamic sort, a search box, a reporting join — assistants abandon the query builder and interpolate strings:
Both are injectable. Values belong in bindings, and identifiers (column names, directions) belong in allow-lists — bindings cannot protect a column name. The safe versions, and the reasons assistants drift into raw SQL in the first place, get a full article: Why AI loves raw queries (and how to stop it).
Blade's {{ }} escapes by default, which makes Laravel hard to XSS — until the assistant needs to render something "rich" and reaches for the raw echo:
Now any user who can save a comment can run JavaScript in every other visitor's browser: session theft, fake login forms, admin actions performed with the victim's cookies. Use {{ $comment->body }} unless the content is genuinely trusted HTML. If users legitimately write rich text, sanitize on output with an HTML purifier, or render Markdown with HTML stripped:
AI-written upload handlers tend to trust everything the client sends: the original filename, the extension, the MIME type, the size.
That single snippet allows overwriting other users' files, dropping a .php file into a publicly executed directory on permissive servers, and storing 2 GB "avatars". Validate strictly, let Laravel name the file, and keep uploads out of the web root unless they must be public:
Two extra notes: treat SVG as code, not as an image (inline scripts make it an XSS vector), and serve private files through a controller or signed URL, never by guessable path.
Vibe-coded apps routinely reach production with APP_DEBUG=true, a committed .env, or env() calls sprinkled through application code. Debug error pages hand an attacker stack traces, request data and config; a .env served by a misconfigured web root hands them everything else. Two checks right now:
The full set of production settings — sessions, cookies, CORS, Telescope gates, caching — is in Turning off the footguns: production config for Laravel.
Assistants wire up login, registration, password reset and OTP endpoints without any throttle. That means free credential stuffing, free user enumeration, and a mail bill from password-reset spam. Laravel's limiter makes the fix small:
Throttle anything that sends email or SMS, anything that checks a credential, and any expensive endpoint a bot could hammer.
It is not randomness. Three forces push the same direction:
$request->all() appears in thousands of examples.The result is code that demos perfectly and fails adversarially. None of this means you should not ship AI-written code; it means the review step that a senior engineer used to do implicitly now has to happen explicitly.
A grep pass catches a surprising share of the above. Run these from the project root:
Every hit is not automatically a vulnerability — but every hit deserves thirty seconds of attention. For a structured pass with two-account testing and severity calls, follow our pre-launch review checklist.
If an assistant wrote a meaningful part of your app, a short, senior AI-code security audit pays for itself the first time it catches one of the seven above. Every finding we report comes with a concrete fix, and you can see a sample report before booking anything.
It fails differently. Human teams accumulate fewer, weirder bugs; assistants produce the same handful of textbook flaws at high frequency — mass assignment, missing authorization, raw SQL. That regularity is good news: a focused review catches most of it quickly.
Missing authorization. Injection and XSS need a payload; an IDOR needs only a changed ID in the URL, and it directly exposes other customers' data — which is usually a reportable breach, not just a bug.
Tools like Larastan and security scanners flag some patterns (raw SQL, debug config). They cannot tell whether this invoice should be visible to that user — authorization is business logic, and it is exactly where AI code fails most. Pair tooling with a human pass over every route.
A senior engineer reviews every line the assistant wrote. Fixed price, every finding with a fix.