The AI app security checklist

Whatever built it โ€” Lovable, Bolt, v0, Cursor, Claude Code โ€” the same handful of problems come up again and again. Here they are in the order worth fixing them.

These aren't obscure vulnerabilities. They're the consequences of two facts that are obvious once you know them and invisible until you do.

One. Browser code cannot keep a secret. Anything in the frontend โ€” hardcoded, in a NEXT_PUBLIC_ variable, inside an SDK running client-side โ€” is readable by every visitor. Not obscure. Public.

Two. Deleting a leaked key doesn't un-leak it. It's in your git history, someone's cache, a scraper's database. The only real fix is rotating it at the provider.

Critical โ€” fix before anyone else sees the app

Secret keys in code that ships to the browser

Stripe secret keys (sk_live_โ€ฆ), AI provider keys, database connection strings with passwords in them. Anything in a frontend file, or in a variable prefixed VITE_ / NEXT_PUBLIC_ / REACT_APP_, is published. Move the call server-side, read the key from a server-only environment variable, then rotate the exposed one.

Database rules turned off

If you're on Supabase or Firebase, your client talks to the database directly using a public key, and the only thing protecting your data is Row Level Security (or Firestore rules). Off, or set to allow-all, means anyone can read and write everything. A Supabase service_role key in client code has the same effect, since it bypasses those rules entirely.

A live .env or .git on your deployed site

If yoursite.com/.env returns a file, every credential in it is public and being scanned for constantly. Same for a served .git directory, which lets anyone clone your whole history โ€” including secrets you committed and later "removed".

High โ€” fix before you have real users

AI APIs called straight from the frontend

dangerouslyAllowBrowser: true is the usual marker. It makes an error go away, the app starts working, and your API key is now in every visitor's browser. Proxy the call through your own backend route and rate limit it, or you're funding strangers' usage.

SQL built by pasting values into strings

A query assembled with an f-string or template literal will happily execute whatever a user types. Use parameterised queries โ€” placeholders with the values passed separately โ€” everywhere, not just where input "looks" untrusted.

No rate limiting on anything that costs money

Any endpoint that sends email, calls an AI model, or writes to your database can be called in a loop by a stranger. This is the one people skip because it isn't a "vulnerability" โ€” it's just a bill.

Medium โ€” fix before you forget

The five-minute version

Or have it checked for you

vibecheck scans your code or your live URL for everything on this page, explains what it finds in plain English, and gives you a fix prompt to paste back into whatever AI tool you're using.

Scan my app โ€” free