Pre-launch security checklist for vibe-coded apps
Practical checklist from ShipSafeStack. Not a penetration test, compliance audit, score, or proof that an app is safe to launch. Informational only — no guarantees.
Run this on the project you are about to share. AI tools often repeat the same shortcuts: a key in the client, a table with no access rule, a price decided in the browser.
Builders this list covers include Lovable, Bolt.new, Cursor, v0, and Replit. Shorter per-builder lists live under Ship guides.
Before anyone else has the URL
- Decide which URL is production and which URLs are previews. Preview links are often public. Treat a preview that has real data as production.
- Read the environment variable list in the host dashboard. Anything the framework ships to the browser is public (for example
VITE_on Lovable,NEXT_PUBLIC_on Next.js). - Search the repo and the built JavaScript for shapes like
sk_live,service_role,sb_secret_,sk-,AKIA, and private key blocks. If you find a real secret, rotate it at the vendor, then remove it. Rotating without removing it leaks the next one. - Confirm the app is served over HTTPS and that you are not shipping a debug page, a stack trace, or a seed script with a known password.
What the model tends to ship
These are patterns vendors document — not a statistic about how often they appear.
-
A key next to a
fetchin the client → confirm it is a publishable value, or move it to a server secret. - A new table with no access policy → row level security (or equivalent rules) exists, and a second user fails to read the first user’s rows.
- A rule that only checks “is signed in” → the rule also checks which rows that person owns.
- A price, plan, or “paid” flag set in the browser → the server records the result from the payment provider’s signed event, not from the success page.
- An admin or service credential in client code → that credential lives only in a server function.
Secrets the browser can see
- Sort publishable keys from secrets. Publishable (when the vendor says so): Stripe
pk_, Supabase anon/publishable with RLS on, public analytics IDs. - Never ship in the client: Stripe
sk_, Supabaseservice_role/sb_secret_, model keys, email provider keys, database URLs with passwords, webhook signing secrets. - Search the deployed site’s JavaScript (network panel →
.jsresponses), not only the source tree. - Check host env screens: a client-prefixed variable is public even if the dashboard looks private.
More detail: API keys leaking into the frontend and How to keep API keys out of AI-generated code.
Data access
- For Supabase: enable RLS, fix default grants, and add per-user policies before a client with the anon key talks to the database. See Supabase RLS for an AI-built app and the RLS checklist.
- Prove a second account cannot read the first account’s rows.
- The anon key in the browser is expected when RLS is correct; a
service_rolekey in the browser is not. See Is the Supabase anon key safe to expose?.
Accounts, money, email, abuse
- Confirm sessions are checked on the server for protected routes.
- Record payments from the provider’s signed webhook, not from a browser “success” page — Stripe webhook verification checklist.
- Keep email/API secrets on the server.
- Put rate limits on public forms and auth routes on the server, not only by disabling a button — Rate limiting checklist.
After you have read the code
- Re-check the production URL after deploy (headers, HTTPS, no debug pages).
- Rotate anything that appeared in chat transcripts, screenshots, or old preview deployments.
- If a Supabase service role key leaked: what to do now. If a GitHub token leaked: next 15 minutes.
Soft reminder
A green scan from a builder is not the same as finishing this list. Builder scans help; they are not a substitute for checking secrets, RLS, payments, and abuse controls yourself.
Check a public URL (passive, free): shipsafestack.com/scan — ordinary GETs only; nothing stored; informational only, not an audit.
Top comments (1)
"Treat a preview that has real data as production" is the item most people skip. Preview URLs get shared in chats and end up indexed. One addition: check the git history too, not just the current tree, since a secret deleted in a later commit is still in every clone.