The AI builder got you to a working demo in a weekend. That is a real achievement. But the same optimisation that makes builders fast at shipping features makes them bad at security: they are trained to make things work, not to make them safe for strangers on the internet with bad intentions.
Most vibe-coded apps have between four and eight security issues significant enough to cause a real problem in production. Some are embarrassing. A few are the kind that end up in incident postmortems. This checklist covers the 18 most common ones, organised by category. Work through it before you put real users or real money on your app.
Who this is for. Founders who built with Bolt, Lovable, Cursor, v0 or Replit and are approaching a real launch. Not a theoretical exercise for a toy project: a production app with customers, payments, or personal data that needs to survive contact with the internet.
If you fail more than three items in the Authentication or Data Exposure sections, stop and get a proper audit before you go live. Those failures are not theoretical risks.
Authentication and sessions
Authentication is the first thing every AI builder gets wrong at scale. The demo works because the developer is the only user. Add a second user and the cracks appear immediately.
| Check | What to look for | Why it matters |
|---|---|---|
| 1. Supabase Row Level Security is enabled and has policies | Open your Supabase dashboard. Every table should show RLS enabled. Each table should have at least one policy. An enabled RLS with no policies blocks all access. No RLS at all means every authenticated user can read every row. | This is the most common critical finding in vibe-coded app audits. Lovable, Bolt and v0 all use Supabase by default and the model does not write RLS policies reliably. |
| 2. No service-role key in client-side code | Search your codebase for service_role. It should only ever appear in server-side environment variables, never in client-side JavaScript, React components, or .env files that get bundled into the browser. |
The service-role key bypasses RLS entirely. A user who finds it in your browser bundle can read and modify every row in your database. |
| 3. Admin routes require a server-side auth check | Any route that exposes user data, admin functions, or bulk operations should verify the session on the server before returning data. Client-side redirects ("if not logged in, go to /login") are not access control. | Client-side guards protect the interface, not the data. Anyone who calls your API directly bypasses them completely. |
| 4. Sessions expire | Check your auth provider settings. Sessions should expire after a reasonable period of inactivity. Supabase defaults to one hour with refresh tokens; make sure you have not extended this indefinitely. | A session token that never expires is a permanent credential. If it leaks, you have no natural recovery mechanism. |
| 5. Rate limiting on auth endpoints | Test whether you can call your signup, login, and password-reset endpoints rapidly without any slowdown or block. Supabase has per-email limits; custom auth implementations often have none. | Without rate limiting, your signup form is a user-enumeration tool and your login form accepts brute-force attacks. |
Data exposure
The most common source of data breaches in early-stage apps is not sophisticated hacking. It is credentials left in places they should not be, combined with default settings that were never changed.
| Check | What to look for | Why it matters |
|---|---|---|
| 6. No .env file committed to your repository | Run git log --all -- .env in your project. If it shows any commits, your credentials are in your git history even if the file is in .gitignore now. You need to rotate every secret that was ever in that file. |
Git history is permanent. Deleting the file does not remove it from the history. Public repositories mean those credentials are fully public, indexed by GitHub search and various credential-scanning bots. |
| 7. No API keys hardcoded in frontend JavaScript | Open your deployed app. View source or open the browser devtools Network tab. Search for sk-, secret, or your known API key prefixes. Anything in your browser bundle is visible to every visitor. |
Hardcoded API keys are found and abused within hours of a public launch. The cost of an abused OpenAI or Stripe key can reach thousands before you notice. |
| 8. Storage buckets are not public by default | Check your storage provider settings. Supabase buckets default to private; Vercel Blob, AWS S3 and others have different defaults. If you created a bucket and did not explicitly set permissions, verify the current state. | A public storage bucket exposes every uploaded file to anyone with the URL. That includes user profile pictures, documents, payment receipts, and anything else your users uploaded. |
| 9. Sensitive data is not logged to the console | Search your codebase for console.log. Check what is being logged. Passwords, tokens, credit card data, and full user objects should not appear in logs that developers (and potentially logging services) can read. |
Console logs often get shipped to third-party monitoring services. Personal data in those logs creates compliance problems and breach notification obligations. |
| 10. User passwords are not stored in plain text | If you built a custom auth system, confirm that passwords are hashed with bcrypt, argon2, or equivalent before storage. If you used a provider like Supabase Auth, this is handled for you. Custom implementations miss this regularly. | A plain-text password database is not a security incident waiting to happen: it is a criminal record waiting to be filed. |
API and backend logic
The backend is where vibe-coded apps get subtle. The app appears to work correctly because every test case was written by the same model that wrote the code. Adversarial inputs were never tested.
| Check | What to look for | Why it matters |
|---|---|---|
| 11. Input validation on every API endpoint | Pick three API endpoints. Try calling them with missing fields, unexpected types, strings where numbers are expected, and very long strings. Do they return controlled errors or do they crash, return internal data, or accept the bad input silently? | No input validation is the root cause of SQL injection, broken object-level authorisation, and a range of logic bugs that only appear under real-world use. |
| 12. CORS is not set to wildcard | Check your API configuration for Access-Control-Allow-Origin: *. A wildcard CORS policy means any website on the internet can make authenticated requests to your API using your users' credentials. |
Combined with other auth weaknesses, wildcard CORS is the prerequisite for cross-site request forgery attacks against your users. |
| 13. Stripe and other webhooks verify the signature | Find your webhook handler. It should call stripe.webhooks.constructEvent (or your provider's equivalent) with the raw body and the signature header before doing anything with the payload. |
Without signature verification, anyone can POST a fake "payment succeeded" event to your webhook endpoint and get free access to whatever your app grants on payment. |
| 14. Prices and amounts are calculated on the server | Check your checkout flow. The amount passed to Stripe should be calculated on the server from your database prices, not taken from a form field or a client-side calculation that a user could modify. | Charging a user-submitted amount means any customer can pay $0.01 for a $200 product by editing a form field or intercepting the network request. |
| 15. Users can only access their own data | Create two test accounts. Log in as User A and attempt to access User B's records by changing an ID in the URL or API request. Can you see User B's orders, profile, or documents? | Broken object-level authorisation (BOLA) is the most common API vulnerability in AI-generated code. The model writes endpoints that check authentication but not ownership. |
Infrastructure and dependencies
These checks are less likely to cause an immediate incident but are the difference between an app that runs for years and one that degrades into an unmaintainable mess after six months.
| Check | What to look for | Why it matters |
|---|---|---|
| 16. Production uses different credentials than development | Confirm that your production database, storage, and third-party services are separate from your development environment. Development credentials should not have access to production data. | A misconfigured environment variable that points dev tooling at the production database is a common cause of accidental data deletion. |
| 17. Error messages do not expose internal state | Trigger a few expected errors in your app (wrong password, missing field). What does the user see? Stack traces, SQL queries, file paths, and internal identifiers should not appear in error responses. | Information disclosure helps attackers understand your tech stack and find exploitable patterns. It also looks amateur to the users who see it. |
| 18. Critical dependency vulnerabilities are addressed | Run npm audit in your project. Review the critical and high severity items. Not every advisory is exploitable in your context, but every critical item deserves a decision: patch, suppress with justification, or accept the risk. |
Transitive vulnerabilities (vulnerabilities in packages your packages depend on) are a common blind spot. The model picks packages that work, not packages that are maintained. |
What to do with the results
Count your failures by category.
Zero failures in Authentication and Data Exposure, fewer than three overall: Your app has a reasonable baseline. Keep running this checklist as you add features. Every new endpoint is a new opportunity to introduce one of these issues.
One or two failures in Authentication or Data Exposure: Those items are blocking issues before a public launch. Address them before anything else. If you are not sure how to fix them correctly, get help: an incorrectly patched RLS policy can silently leak data while appearing to work.
Three or more failures across the list: A systematic pass is faster and cheaper than fixing items one by one. The issues do not appear in isolation: a codebase that has broken object-level authorisation usually has other auth issues in the same endpoints. A vibe-code rescue audit covers all of these systematically, produces a fixed-scope findings report before anyone touches your code, and finishes with the fixes applied and tests written.
The audit is the first step, not the last. You get a report with every finding, a fixed quote to fix it, and then you decide. No surprise invoices, no hourly meter.
The existing guides for each specific tool walk through the most common failure patterns for that builder: Lovable, Bolt, Cursor, v0, and Replit. The security issues above apply across all of them, but each builder has its own failure signature.
Frequently asked questions
Can I run these security checks myself?
Some of them, yes. Checking whether your .env file is in git history, whether your storage buckets are public, and whether you are console-logging sensitive data are all visible without deep technical knowledge. Row Level Security policies, CORS configuration, webhook signature verification, and server-side validation require understanding your specific codebase. A professional audit finds the ones you cannot see.
How many failed checks mean I need a rescue?
One failed check in the Authentication or Data Exposure categories is enough to warrant an audit before a public launch. Those failures are not theoretical risks. They are the mechanisms behind real data breaches. If you fail four or more checks across any categories, a ground-up security pass is cheaper than the alternative.
Is Supabase RLS really that commonly broken?
Yes. Every Supabase project ships with Row Level Security disabled by default. The AI builders that use Supabase under the hood get you a working app, but the model is optimising for demonstrating features, not for locking down access. Enabling RLS without writing correct policies breaks the app. Writing incorrect policies silently leaks data. It is the single most common critical finding in our audits.
What does a professional audit cover that this checklist does not?
This checklist covers the issues that are visible from outside the codebase or from reading the code at a high level. A proper audit goes further: it tests your RLS policies against real query patterns, checks your dependency graph for transitive vulnerabilities, reviews your webhook handling for replay attacks, tests for broken object-level authorisation across your API endpoints, and verifies that your error handling does not expose internal state. It then produces a fixed-scope findings report before anyone touches your code.
Can I keep using Bolt, Lovable or Cursor after a rescue?
Yes. The rescue puts a proper foundation underneath your app: version control, tests, CI, correct auth. After that, you can keep prompting in the builder for features. One bad generation can no longer take down production, because there are guardrails underneath.