Vibe-coded app security checklist: 6 things to check before you launch
Published · by Steffen Stein, Zybee Labs
AI coding tools such as Lovable, Bolt, Replit and Cursor get an app from idea to live URL in an afternoon. They build what you ask for. Security settings you never asked for are easy to leave out, and nothing breaks visibly when they are missing.
This checklist covers six areas, in the same order we use when we review an app from the outside. Each section says what can go wrong and how to check it yourself. Only run these checks on apps you own or are authorised to test.
The short version:
| # | Check | What you are looking for |
|---|---|---|
| 1 | Public footprint | Forgotten preview deployments, staging sites and subdomains |
| 2 | Exposed keys | Secret keys in the JavaScript your visitors download |
| 3 | Database exposure | Supabase tables without Row Level Security, open Firebase rules |
| 4 | Files that should not be public | .env files, .git folders, backups, database exports |
| 5 | Everyday protections | HTTPS everywhere, browser security headers |
| 6 | Outdated components | Libraries and frameworks with known security issues |
1. Know your public footprint
You cannot protect what you forgot exists. Vibe-coded projects tend to leave a trail: preview URLs from the builder, a staging copy on another host, an old version on a subdomain, an admin tool someone set up once.
How to check:
- List every URL your app has ever been published to: the builder’s own hosting domain (for example a
*.lovable.appaddress), Vercel or Netlify preview URLs, and your custom domain. - Look up your domain in a certificate transparency search such as crt.sh. It lists the subdomains that have been issued HTTPS certificates, which often surfaces ones you forgot.
- For each address that still answers: does it need to be public? Does it connect to the same database as production?
Fix: delete deployments you no longer need, or put them behind a password. A forgotten staging copy usually has the same database access as the real app, with less attention paid to it.
2. Look for exposed keys in your frontend
Everything in your frontend code ships to every visitor. When an AI tool wires up a payment provider, an email service or an AI API directly from the browser, the key often ends up in a public JavaScript file.
Not every key is a secret. Some are designed to be public:
| Usually fine in the frontend | Must never be in the frontend |
|---|---|
Supabase anon / publishable key (sb_publishable_…) | Supabase service_role / secret key (sb_secret_…) |
Firebase apiKey | Firebase service account (private key JSON) |
Stripe publishable key (pk_live_…) | Stripe secret key (sk_live_…) |
| OpenAI, Anthropic or other AI provider keys | |
| Email, SMS or database passwords |
How to check:
- Open your live site in Chrome or Firefox and open the developer tools (F12).
- Use “Search all files” (Ctrl+Shift+F, or Cmd+Option+F on a Mac) and search for
secret,sk_,sb_secret_,service_role,Bearer,passwordand the name of each service you use. - Check your public Git repository too, if you have one, including its history. A key deleted in a later commit is still in the history.
Fix: a key that was public must be treated as compromised. Revoke it with the provider and create a new one. Move the call that needs it into a server-side function (Supabase Edge Functions, a Vercel or Netlify function, a Cloudflare Worker) and keep the new key in a server-only environment variable. Then rebuild and search the public files again to confirm it is gone.
3. Check your database access rules
This is the issue we would put first for most AI-built apps. Supabase and Firebase let the browser talk to the database directly. That is safe only when access rules decide who may read and write what. Without them, anyone with the public key, which is everyone, can read the data.
Supabase:
- Every table in the
publicschema needs Row Level Security enabled, plus policies that match your intent (“users can read their own rows”, not “anyone can read everything”). - Open the Supabase dashboard and review the security advisor (under Advisors), which flags tables without Row Level Security.
- Look out for policies with
using (true)on tables holding personal data. That is the same as no protection. - Check Storage buckets: a public bucket serves every file to anyone with the URL.
Firebase:
- Open your Firestore, Realtime Database and Storage rules. Rules such as
allow read, write: if true;or test-mode rules with an expiry date leave data open to everyone. - Rules should check
request.authand that the user owns the document.
How to test from the outside: sign out, open the browser developer tools, and watch the network requests while you use the app. Then try loading another user’s record by changing an ID in a request. If you get data you should not see, your rules are not doing their job. Use your own test accounts and do not read real customer records.
Fix: ask your AI coding tool for exact rules (“enable Row Level Security on the orders table and add a policy so users can only select rows where user_id = auth.uid()”), then repeat the signed-out test.
4. Find files that should not be public
Small deployment mistakes can publish files that were only meant for your machine. The usual suspects:
.envfiles with API keys and database passwords- a
.gitfolder, which can expose your entire source code and history - backups and exports:
backup.zip,db.sql,export.csv - source maps (
.js.map) that reveal your original source code - admin pages or debug tools left reachable without a login
How to check: in your browser, try https://yourdomain.com/.env and https://yourdomain.com/.git/config. Both should return “not found”, not file contents. Then check your hosting project’s file list for anything that is not part of the app.
Fix: delete the files from the deployment and rotate any credentials they contained. Exclude them from future builds (.gitignore, and your host’s ignore or build settings).
5. Turn on everyday protections
These settings do not fix bugs in your app, but they close off whole classes of attack, and most hosts make them easy.
- HTTPS everywhere:
http://should redirect tohttps://on every domain and subdomain. - Strict-Transport-Security (HSTS): tells browsers to use HTTPS only.
- Content-Security-Policy: limits where scripts can load from, which reduces the damage of injected code.
- X-Content-Type-Options: nosniff and a frame policy (
frame-ancestorsin your CSP, or X-Frame-Options) to stop your app being embedded by other sites.
How to check: open the developer tools, go to the Network tab, reload, click the first request and read its response headers.
Fix: most hosts let you set headers in a config file (for example vercel.json, netlify.toml or a _headers file). Add a strict Content-Security-Policy step by step and test the app afterwards, because a policy that is too strict blocks your own scripts.
6. Keep components up to date
AI tools pick a library version when they generate code. Months later that version may have a known security issue, and nothing in the app tells you.
How to check: run npm audit in the project (or ask your AI tool to run it and explain the results). Check that your framework is on a supported version.
A version number alone does not prove your app is exploitable. Many issues only apply to features you do not use. Treat audit results as a list of things to look at, and update critical items first.
Where to start
If you only have an hour, do checks 2 and 3 first. Exposed secret keys and open database tables are the two issues most likely to cause real harm, and both are quick to verify on your own app.
Write down what you checked and when, then repeat after every major change. Each new feature your AI tool builds can add a new table, a new key or a new deployment.
Questions
Are apps built with Lovable, Bolt or Cursor insecure?
Not by default. The tools generate working code fast, but they build what you ask for. If nobody asks for access rules, server-side secrets or a review of what is public, those parts are easy to miss. The checks on this page cover the most common gaps.
Is my Supabase anon key a secret?
No. The anon (or publishable) key is designed to be visible in your frontend. What protects your data is Row Level Security on every table the key can reach. The service_role (or secret) key is different: it bypasses Row Level Security and must never appear in frontend code.
Is a Firebase apiKey in my JavaScript a problem?
Usually not. The Firebase apiKey identifies your project and is meant to be public. Your Firestore, Realtime Database and Storage security rules decide who can read and write data, so those rules are what you need to check.
How do I check my vibe-coded app for exposed API keys?
Open your live site, open the browser developer tools, and search all loaded scripts for terms such as "secret", "sk_", "sb_secret_", "service_role" and "Bearer". Anything that grants server-side access must be revoked, replaced, and moved to a server-side function.
Can I ask my AI coding tool to fix security issues?
Yes, and it often works well when the request is specific: name the issue, the file or table, and the expected behaviour. Afterwards, test the result yourself on the live site, because the tool cannot see your production configuration.