Why vibe-coded apps are more vulnerable

Apps built with Cursor, Bolt, Lovable, v0 or Replit Agent consistently contain the same vulnerability patterns. Not because AI is "bad" — but because AI optimises for working, not secure. A pentester's analysis of the problem and what to do about it.

What is vibe coding?

"Vibe coding" (a term that went viral in 2025) describes a new way of building software: instead of writing code yourself, you describe what the software should do to an AI tool, which then produces working code. Cursor, Bolt.new, Lovable, v0, Replit Agent and Claude Code are the best-known tools. With a good prompt you can have a working MVP in an afternoon.

The effect on product development is dramatic. Solo founders build in weeks what previously took a team of five half a year. Indie hackers go live with production applications without ever manually configuring an ORM, a security header or an authorisation model.

The effect on security is unfortunately equally dramatic — but in the opposite direction.

The pattern we see again and again

In virtually every vibe-coded application I review, the same classes of vulnerability turn up. This is not a coincidence, and it's not incompetence on the builder's part. It's a direct consequence of how AI coding tools work.

1. Missing authorisation on endpoints

The AI generates an endpoint like GET /api/users/:id. Authentication is present: the user must be logged in. But authorisation — the check of whether this user may request this specific user object — is missing. Result: every logged-in user can retrieve any other user account by simply changing the ID in the URL.

Why does the AI miss this? Because the prompt said "add authentication." Object-level access control wasn't in it. The AI has no built-in notion of "who may do what." You have to explicitly spell that out.

2. IDOR via sequential IDs

Related to the above: virtually all vibe-coded apps use auto-increment integer IDs in URLs. Increment ?order=1042 to ?order=1043 and — if the authorisation check is missing (see above) — you see someone else's order, file, project or whatever. This is the classic IDOR vulnerability, and in vibe-coded code it is almost standard.

3. Hardcoded secrets and exposed API keys

Stripe keys in client-side bundles. OpenAI tokens in .env.local files committed to git. Database URLs in server-rendered Next.js pages that accidentally end up client-side via getServerSideProps. Supabase service-role keys publicly accessible.

The reason? The AI places the key where the feature "works first try." That's often the wrong side of the network boundary: server-only secrets end up client-side, or worse, in public git repos.

4. Unsafe database queries on edge cases

Good news: 80% of the time the AI correctly uses an ORM with parameterised queries. Bad news: for the other 20% — especially filter, search and advanced query features — user input is suddenly concatenated directly into a query string. Classic SQL injection, on the last feature you expected.

5. Overly permissive CORS

Access-Control-Allow-Origin: * combined with credentials: 'include'. Works perfectly, and gives any arbitrary website permission to make requests to your API on behalf of your logged-in users. A fatal combination that no scanner flags as critical — configuration- and usage-specific, and therefore invisible to automated tools.

Why does the AI do this? Because the prompt was "fix this CORS error." The AI resolves it with the most permissive configuration that makes the error go away.

6. Admin routes that are only protected client-side

The React app has a neat route-guard component that checks whether user.role === 'admin'. Want to bypass it? Open the browser dev-tools, or make a direct request to the API endpoint. Server-side there's no check. The admin route was visually protected, not actually protected.

7. Supabase/Firebase with unprotected tables

Supabase and Firebase are enormously popular in vibe-coded apps, rightly so, as they scale well and the AI model understands them. But Row-Level Security (RLS) is off by default in Supabase. The AI creates tables, data flows, and nobody turns on RLS. Or policies are created that look right but allow cross-tenant data access via filter chains.

8. Prompt injection & AI feature abuse

If your app calls an LLM with user input (a chatbot, a document analysis flow, an AI agent), that's an attack surface. Users can hijack system prompts, extract data from your context, or call tools you didn't intend to expose. Most vibe-coded apps have no defence against this. It doesn't exist by default; you have to explicitly build it.

Why AI consistently gets this wrong

To be clear: these are not bugs in the AI tools. They are the direct consequences of how they work. Three structural causes:

The training data is on average insecure code

AI coding tools are trained on public code repositories. The median level of security in public code is low. Most tutorials, example projects and GitHub snippets have no production-grade security. The AI imitates what it has seen.

The optimisation function is "make it work"

Ask an AI "fix this CORS error" and it chooses the configuration that eliminates the problem. Ask it "let users edit their profile" and it builds an endpoint. It's not trained to ask: "should other users be able to see this? who may do what?" Those are the questions an experienced developer asks. An AI doesn't.

Natural language is inherently ambiguous

When you say "let users edit their profile," you think: "users can only edit their own profile." The AI probably thinks the same, and builds it. Or it doesn't, and builds an endpoint where everyone can edit all profiles. You only know which of the two when you carefully read the generated code. And if you could do that, you'd have written it yourself.

Important to understand

This is not a critique of AI tools or vibe coders. AI tools are extraordinary: they compress months into days. The point is only that shipped functionality ≠ shipped security. And someone has to consciously close that gap.

Why scanners miss these problems

"Just run Snyk, Semgrep or Dependabot" is the standard advice. Useful for known CVEs and library vulnerabilities. But the above pattern is almost entirely about logic, authorisation and architecture. That is:

  • Not pattern-matchable — it depends on your data model and intended access rules
  • Not in any signature database — it's business logic, not a library bug
  • App-specific — a scanner doesn't know who should see what
  • Often spread across multiple files — an attacker chains three small issues into one breach

The only way to find them is for a human to think like an attacker while working through your application. That's what a pentest does.

What you can do — before and during building

Specify authorisation explicitly

For every new feature: include in the prompt "make sure only the owner can read/edit/delete." That one sentence changes what the AI builds. By default the AI doesn't build that check. Ask for it explicitly, and it will.

UUIDs instead of auto-increment IDs

Default: use UUIDs as public resource identifiers. Auto-increment IDs are fine as internal database keys, but don't expose them in URLs. This eliminates IDOR-via-guessing as an entire class of vulnerability.

Turn on RLS — immediately

For Supabase/Firebase: enable Row-Level Security per table at creation, not later. Test policies by querying as a different user. A policy that compiles is not the same as a policy that correctly filters.

Read what the AI builds yourself

This is hard but essential. For every endpoint that touches user data: read the generated code. Look for authorisation checks. Ask the AI: "audit this endpoint for authorization, IDOR, and input validation issues — what's missing?" — and it often finds the problems itself.

Plan a pentest before a serious launch

Before going live with paying customers, before processing personally identifiable information, before an investor does due diligence: have a manual pentest performed. A week's work, fixed price, with a report in plain language. Resync's vibe-coded app security audit is built exactly for this.

Conclusion

Vibe coding is an enormous acceleration — for product development, for solo founders, for experiments. But security is an independent discipline that the AI doesn't do for you. The vulnerability patterns in vibe-coded apps are predictable, and that's good news: predictable problems are solvable.

What doesn't work: hoping it will be fine. What does work: deliberately building security into your prompts, RLS on by default, UUIDs as public IDs, and — before a serious launch — an independent pentest. Building fast and launching securely are not mutually exclusive.

Audit your vibe-coded app.

Manual pentest specifically for AI-generated apps. Fixed price, 1-week turnaround, retest included.

Go to the service page →