← Blog

Vibe-coded app security checklist: 6 things to check before you launch

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:

#CheckWhat you are looking for
1Public footprintForgotten preview deployments, staging sites and subdomains
2Exposed keysSecret keys in the JavaScript your visitors download
3Database exposureSupabase tables without Row Level Security, open Firebase rules
4Files that should not be public.env files, .git folders, backups, database exports
5Everyday protectionsHTTPS everywhere, browser security headers
6Outdated componentsLibraries 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.app address), 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 frontendMust never be in the frontend
Supabase anon / publishable key (sb_publishable_…)Supabase service_role / secret key (sb_secret_…)
Firebase apiKeyFirebase 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:

  1. Open your live site in Chrome or Firefox and open the developer tools (F12).
  2. Use “Search all files” (Ctrl+Shift+F, or Cmd+Option+F on a Mac) and search for secret, sk_, sb_secret_, service_role, Bearer, password and the name of each service you use.
  3. 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 public schema 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.auth and 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:

  • .env files with API keys and database passwords
  • a .git folder, 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 to https:// 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-ancestors in 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.