Blog · Guides · 2026-10-07

Vibe Coding Security Risks: What to Check Before You Ship

TL;DRThe three vibe coding security risks that matter most are API keys exposed in the frontend bundle, database row-level security left off or permissive, and endpoints that check whether you're logged in but not whether you own the record. AI tools scaffold authentication well and authorization almost never, because the prompt described the happy path. Test all three with two accounts and a terminal before launch.

The biggest vibe coding security risks are exposed API keys in client-side code, missing or permissive database row-level security, and absent server-side authorization checks. Each one lets a stranger read or change data that isn't theirs, and all three are easy to miss because the app looks and feels finished. AI coding tools optimise for a working demo, so they scaffold authentication (letting someone log in) while skipping authorization (checking whether that person may touch this specific record) — and they leave development defaults like open CORS, verbose error pages and unlimited request rates in place when you deploy. Before you ship an AI-built app, work through five things in order: secrets, data access rules, per-record permissions, server-side validation, and dependencies.

What are the biggest vibe coding security risks?

These are the failure modes that recur in apps built with Cursor, Bolt, Lovable, v0 and Replit. None are exotic — they're the ordinary mistakes of a developer in a hurry, which is exactly what an AI assistant is.

RiskWhat it looks like in AI-generated codeFast check
Exposed secretsA service key, OpenAI key or SMTP password sitting in a frontend file or a VITE_/NEXT_PUBLIC_ variableOpen devtools → Sources, search the bundle for key, secret, sk-
Open databaseSupabase or Firebase wired up with row-level security off or a blanket "allow all" policyQuery another user's row with your own session token
Broken access controlLogin works, but /api/invoice/123 returns invoice 123 to anyone signed inChange an ID in the URL or request body to one you don't own
Client-only validationRequired fields and length limits enforced in the form, not in the handlerReplay the API call directly with bad data
Dev config in productionCORS set to *, stack traces rendered to the browser, debug flags onTrigger an error on the live site and read the response
No rate limitingSignup, password reset and any endpoint that calls a paid API, all uncappedFire the same request 100 times in a loop

Treat broken access control as your default assumption rather than a possibility. It sits at the top of the OWASP Top 10, and it's the category AI scaffolding is structurally worst at: getting it right requires knowing your data model's ownership rules, which the model infers from a prompt rather than reads from a spec.

Why does AI-generated code have security holes in the first place?

Three reasons, and understanding them tells you where to look.

The prompt describes the happy path. You asked for "a dashboard where users can see their orders." You did not ask "and make sure user A cannot fetch user B's orders by changing a query parameter." The model builds what was described. Security is mostly about the paths nobody described.

Training data is full of tutorials. Tutorial code is written to be short and runnable, so it skips auth middleware, uses permissive CORS, and hardcodes keys with a // replace this comment. That style is well represented in what these models learned from, and it reproduces faithfully.

Working and safe look identical from the outside. A broken access control bug produces no error, no console warning and no failed test. The feature works. It just also works for the wrong person. This is the same reason AI-written code needs a different review posture than hand-written code — we go into it in more depth in is vibe-coded code safe to ship.

How do I find exposed API keys and secrets?

Anything in your frontend bundle is public. Not "obscure" — public. The build output is served to every visitor, and a minified variable name is not protection.

  1. Load your deployed site, open devtools, and search the bundled JS for secret, service_role, sk-, password, and your provider's key prefixes.
  2. Check every environment variable with a public prefix. In Vite, VITE_* is compiled into the bundle. In Next.js, NEXT_PUBLIC_* is. AI tools reach for those prefixes because they make the error go away.
  3. Check your git history, not just your working tree. A key committed once and deleted later is still in the history and still needs rotating.

The fix is always the same shape: move the call that needs the secret to a server route or edge function, and give the browser only your publishable key. If a key was ever in a bundle or a public repo, rotate it — removing it does not un-leak it.

Is my database actually open to anyone?

This is the risk with the worst blast radius, because it bypasses your app entirely. Managed backends like Supabase and Firebase are designed to be queried straight from the browser using a publishable key, with row-level security policies as the thing that stops you reading everyone's data. If those policies are off, or set to a permissive "authenticated users can select everything" rule to get past an error during the build, then anyone who signs up can read your whole table.

Test it the way an attacker would: sign up two accounts, then use account A's session to request a record that belongs to account B. Do it against the API directly, not through your UI — your UI will politely only ask for the right rows. If B's data comes back, you have a data breach waiting for someone curious.

Does my app check authorization, not just login?

Walk every endpoint and every server action and ask one question: does this verify that the signed-in user owns the thing they're acting on? Not "is there a user" — that's authentication, and the AI almost certainly handled it. Ownership is the gap.

The high-risk spots, in rough order:

  • Anything that takes an ID from the client — ?id=, a path segment, a body field.
  • Delete and update handlers, which often filter by ID alone instead of ID and owner.
  • Admin routes protected by hiding the nav link rather than by a role check on the server.
  • File downloads served from a public bucket with an unguessable-looking path.
  • Webhook receivers that act on a payload without verifying its signature.

Fix these by making ownership part of the query, not a separate if statement — where id = ? and owner_id = ? fails safe in a way an early-return check does not.

What about validation, rate limits and error messages?

Validation written only in the form is decoration — the request can be replayed with any payload at all. Every constraint that matters (required fields, lengths, allowed enum values, numeric ranges, file types and sizes) has to be re-checked in the handler. Schema validation on the server closes most of that gap in one pass.

Rate limits matter for two reasons: brute force against login and password reset, and cost — an uncapped endpoint that calls a paid model API is a bill with no ceiling.

Finally, turn off verbose errors. Scaffolded apps ship with development error handling, which renders stack traces, file paths, SQL and sometimes environment values straight into the browser. That's a free map of your backend.

Can I trust the packages my AI assistant installed?

Mostly, but verify two things. AI assistants sometimes suggest package names that don't exist or are abandoned, because the name pattern looked plausible in training data — and attackers have started registering those plausible-but-nonexistent names on purpose. Pinned versions in generated code can also be well behind current, which means known vulnerabilities. Run your package manager's audit command, confirm every dependency in your manifest is one you can find and recognise on the registry, and remove anything installed during a debugging detour and never used.

How do I keep checking after launch?

A pre-launch pass catches the holes you have today. The problem is that every AI-assisted change after launch can open a new one, and you are shipping faster than you can re-audit by hand. That's the structural gap: code velocity went up, verification didn't.

Two things close it without adding process you'll abandon. First, make broken behaviour easy to report with full context, so the evidence arrives with the bug instead of after three rounds of "what were you doing?" — that's what Snap does. Second, have something exercise your app as different users on a schedule, which is how ownership bugs surface: Sims runs AI personas through real flows, including the paths nobody wrote a test for. For the wider picture of where automated checks fit around AI-written code, start with our complete guide to AI QA, and use the vibe coding QA checklist for the functional side of a pre-launch pass.

The short version

Assume your AI-built app authenticates correctly and authorizes nothing. Assume anything in the bundle is public. Assume your database policies are more permissive than you remember. Then test those three assumptions with two accounts and a terminal, before a stranger does it for you.

Try Klavity free — catch the bugs in your AI-generated code before your users do.

Key takeaways

  • Search your deployed bundle for secrets and rotate anything you find
  • Test row-level security with two accounts against the API directly
  • Make ownership part of every query, not a separate if-statement
  • Re-validate every input server-side and cap your paid-API endpoints

FAQ

What is the most common security risk in vibe-coded apps?

Broken access control — an endpoint that verifies you're signed in but not that you own the record you're requesting. AI tools reliably scaffold login, because that's an explicit feature you asked for. Per-record ownership checks depend on your data model's rules, which the model infers rather than reads, so they're routinely missing. Test it by changing an ID in a request to one belonging to another account.

Are API keys in my frontend code actually exposed?

Yes. Anything compiled into your frontend bundle is served to every visitor and readable in devtools; minification is not protection. Variables with public prefixes like VITE_ or NEXT_PUBLIC_ are compiled in by design. Move any call needing a privileged key to a server route, and rotate any key that was ever in a bundle or a public repo — deleting it does not un-leak it.

How do I check if my Supabase or Firebase database is open?

Create two accounts, then use the first account's session token to request a record owned by the second — directly against the API, not through your UI. If it returns data, your row-level security policies are missing or too permissive. This is common in AI-generated apps because disabling or loosening a policy is the fastest way to clear an error during the build.

Do I need a security audit before launching an AI-built app?

A formal audit is proportionate to what you handle — payments, health data or other people's customer records raise the bar. For most apps, a focused self-check first catches the highest-impact issues: secrets in the bundle, database access policies, per-record authorization, server-side validation, production error verbosity, and rate limits on anything expensive.

Catch bugs the moment a human sees them

Klavity: right-click bug reports, AI personas that review your product, and self-healing tests.

Get started free