The vibe coding security guide
What "vibe coding" means
Vibe coding is the workflow where you describe an app in English to an AI builder — Lovable, Bolt.new, v0, Replit Agent, Windsurf — and ship the generated code with minimal review. The shipping cycle compresses from weeks to hours. The output is usually a frontend wired to Supabase, Firebase, or a similar BaaS, deployed to a CDN.
The term is mostly affectionate; the apps it produces work. The problem is what the AI doesn't generate, not what it does generate. Specifically: the security boundaries that protect the database from the frontend.
The four systemic failures
Every public scan of vibe-coded app populations finds the same shape of failures. Wiz published a study in March 2026 covering Lovable specifically; SymbioticSec scanned 1,072 vibe-coded apps; SupaExplorer indexed 20,000 indie launch URLs. The categories converge:
1. Client-side authentication
The AI generates a login form whose validation logic lives in the browser. The classic shape is a hardcoded admin password in JavaScript:
if (input.password === 'admin123') { setIsAdmin(true); }
Anyone with view-source defeats this in five seconds. Wiz's blog post covers Lovable apps where the entire authentication system was browser-side. The fix is server-side auth: Supabase Auth, Clerk, Auth.js, or your own backend session — anything where the validation runs on a server you control.
2. Exposed API keys
The AI gets prompted with "wire up Stripe" and the user pastes their Stripe key. The AI doesn't know which key it is. If the user pastes sk_live_, that key lands in the React component, which lands in the bundle, which lands in every visitor's browser. Same pattern with OpenAI, Anthropic, AWS, GitHub, SendGrid.
SupaExplorer found the same shape with Supabase: 11% of indie launches expose Supabase credentials in frontend code, with a meaningful fraction containing service_role keys — the keys that bypass Row-Level Security entirely. We covered the response plan in your service_role key is in your client bundle.
3. Disabled or permissive Row-Level Security
This is the highest-volume failure. Supabase tables don't enable RLS by default. Most AI builders don't generate RLS policies. The result: a frontend that talks to the database with the anon key — which is supposed to be public — but the database has no policies governing what the anon key can read.
SupaExplorer's January 2026 scan of 1,645 Lovable showcase apps found 10.3% had critical RLS failures. That's CVE-2025-48757, CVSS 8.26. The classic exploit is a single curl request:
curl 'https://<ref>.supabase.co/rest/v1/users?select=*' \
-H 'apikey: <anon-key-from-bundle>'
If RLS isn't configured, that returns every user row. The Moltbook breach in January was exactly this — the social network launched on January 5th, was breached on January 8th, and 1.5 million tokens plus 35,000 emails were pulled out using the anon key against a database with no RLS.
4. Unauthenticated internal apps deployed publicly
Vibe coders frequently build internal admin dashboards or analytics tools meant only for themselves, then deploy them to a public URL. The dashboards have no auth at all — the assumption is "no one knows the URL." But Cloudflare Pages preview URLs end up in robots.txt scrapers, public deploy logs, and the Wayback Machine, which means they're not secret.
Wiz specifically called this out: internal Lovable-built admin tools, complete with full database read/write privileges, indexed by Google.
What a real breach looks like
Moltbook is a useful study in the standard shape. Public timeline as reported:
- January 5th: launch announcement on Twitter, ~3,000 signups in the first 24 hours.
- January 6th: a security researcher fingerprints the stack from view-source — React + Supabase, anon key visible in
main.js. - January 7th: same researcher tests the obvious tables.
usersreturns rows.auth_tokensreturns rows. RLS is not enabled. - January 8th: 1.5M tokens and 35K emails dumped to a public paste site, with a write-up linking the cause to the unconfigured RLS.
What the team didn't do: scan the deployed app for exposed credentials and open tables before launch. A scan would have flagged this in about ten seconds.
Platform-specific failure modes
The four categories above apply everywhere. The specific shape they take depends on the platform:
- Lovable: most often #2 (key in env var with the wrong prefix) and #3 (RLS not enabled). See the Lovable security checklist.
- Bolt.new (StackBlitz): most often #2 (env var pasted into
src/rather than.env) and #4 (preview URLs deployed without auth). Coverage coming. - v0 by Vercel: most often #1 — generated React Server Components that look server-side but accidentally bundle into client. Coverage coming.
- Replit Agent: most often the Secrets system being correctly set but referenced wrong. Coverage coming.
The five-stage workflow to ship without leaking
Stage 1 — Design with the right boundaries (5 minutes)
Before any AI builder generates code, decide:
- Who can read what? (anonymous visitors, logged-in users, only the owner, only admins)
- Who can write what?
- Which keys need to exist server-side vs client-side?
For most apps, the answer for tables is "the owner can read and write their own rows; nothing else is exposed." Five minutes here saves the rotation cycle later. Write it on a sticky note. The AI doesn't know your data model; you do.
Stage 2 — Generate with the right prompts
The prompt that works:
Build [thing]. Use Supabase Auth for authentication. Enable Row-Level Security on every table I create. Generate a policy for each table that scopes access by auth.uid(). Never put the service_role key in any file that imports from a component.
The prompt that doesn't:
Build [thing] with Supabase.
Both AIs will produce something that runs. Only the first will produce something that's safe to deploy. This is the same as any other dev work — you get what you ask for.
Stage 3 — Review the diff
Before deploying:
- Open every
.env,.env.local, and any file undersrc/that contains a secret-shaped string - Search the codebase for
service_role. It should appear nowhere except possibly server-side / Edge Function code - Search for
NEXT_PUBLIC_,VITE_, or any env-var prefix that exposes to client. None of those should be a secret - Open Supabase Dashboard → Database → Tables. Every table you created should show "RLS enabled"
Stage 4 — Scan the deployed URL
Deploy to a preview URL first. Run a scan. We built vibecheck for exactly this stage — it's read-only, takes about ten seconds, and tells you specifically what's exposed.
The CLI version supports CI gating:
vibecheck https://my-preview-url.com --exit-on critical
Add that to your deploy hook. The deploy fails if the bundle leaks anything critical.
Stage 5 — Monitor on every push
One scan before launch isn't enough. The next AI-assisted change to your codebase can re-introduce the same bug. The fix is automated scans on every deploy. Our GitHub App (in development) does this — connect your repo, get a scan on every push to main, with auto-PRs for any RLS policies the scanner can infer.
Tools landscape
The tools serving this space, ranked by who they're built for:
- vibecheck (this site): solo devs and small teams. Free scanner, autofix workflow, monitoring. Read-only.
- Wiz: enterprise security teams. Has visibility into your whole cloud; expensive; not aimed at solo devs.
- Snyk Code: source-code SAST. Misses runtime issues like exposed keys or open RLS that only show up in deployed bundles.
- Supabase native: in 2026 Supabase added security advisor, automatic key revocation when GitHub leaks are detected, and RLS lints. Use these. They don't replace external scanning, but they reduce the surface.
- SupaExplorer: research project, not a product. Useful reading for understanding the threat landscape.
The case for fixing this now
Two reasons it's worth the half-hour investment:
First, breaches are public. The Moltbook write-up was on Hacker News for two days; the founders haven't recovered the trust. If you launch, get traction, and then leak, the first thing future users find when they Google your company is the breach. The second thing is your apology post.
Second, the cost of preventing the leak is much lower than the cost of cleaning it up. Rotation, breach notification, lost users, lost trust, possibly legal exposure under GDPR / CCPA / state laws — the actual hours of "fix the security issue" become weeks of "manage the consequences."
The five-stage workflow is fifteen minutes plus a recurring scan-on-deploy. That's the entire trade.
Inspect your app