Vibe coding security: what actually goes wrong
Apps built by prompting ship a recognisable set of holes — not because the model is careless, but because nobody asked it the boring questions. This is the list of what really breaks, and the checklist that closes each one.
Why AI-built apps share the same holes
A coding agent optimises for the demo working. Security is invisible in a demo: the app with RLS off renders exactly like the app with RLS on — until someone else’s data shows up. So the same failure modes repeat across Bolt, Lovable, v0, Cursor and Claude-built apps, and they are worth naming precisely, because every one of them is checkable.
- The database trusts the browser. Supabase queried directly from the client with RLS disabled or a logged-in-means-everything policy. Our RLS guide covers the fix end to end.
- Secrets in the bundle. A key pasted where the model saw other keys — NEXT_PUBLIC_ env vars shaped like secrets, the service-role key in client code, a connection string with the password embedded.
- Debug artifacts in production. Dev mode flags in prod config, source maps shipped to every visitor, verbose error pages narrating your schema.
- Endpoints that skip the auth question. Edge functions deployed with verify_jwt off, public POST routes with no rate limit, webhooks that never verify a signature.
- TLS turned off to make an error go away. rejectUnauthorized: false and friends — pasted from a five-year-old answer, shipped to prod.
- A repo with no guardrails. Branch protection that blocks nothing, secrets that scanning already found and nobody rotated.
The hardening checklist, by platform
Supabase
- RLS enabled on every public-schema table, policies per command and role — the cookbook.
- Service-role key server-side only; exposed schemas audited; SSL enforced; network restrictions on.
- Every edge function either verifies JWTs or verifies a provider signature — nothing simply open.
Vercel & the frontend
- No secret-shaped
NEXT_PUBLIC_variables; productionBrowserSourceMaps off unless guarded; previews not serving staging data to the internet.
GitHub / GitLab
- Branch protection with teeth, branches deleted on merge, secret scanning on with a rotation habit, Dependabot findings triaged.
Cloudflare
- Always Use HTTPS on, TLS 1.3 on, no wildcard DNS you didn’t mean, Browser Integrity Check considered.
Make it continuous, not a launch-day ritual
Every item above drifts. The table added next sprint ships without RLS; the debug flag comes back with a hotfix; the new function skips the auth check. A checklist you run once is a snapshot — the failure mode is everything that changes after it. That is the entire reason lumioguard exists: it runs 323 checks like these against your repo and your live services on every scan, and the fixes arrive as pull requests you review, not tickets you triage.
Run the checks on your app
Connect your repo and live services read-only. First scan is free — see every one of these findings on your own app in minutes.