DEV Community

ShipSafeStack
ShipSafeStack

Posted on Originally published at shipsafestack.com

Pre-launch security checklist for vibe-coded apps

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 fetch in 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_, Supabase service_role / sb_secret_, model keys, email provider keys, database URLs with passwords, webhook signing secrets.
  • Search the deployed site’s JavaScript (network panel → .js responses), 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

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.

Original: Pre-launch security checklist for vibe-coded apps

Top comments (1)

Collapse
 
gautamtalksdev profile image
Gautam K •

"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.