Launch and other stacks
Pre-launch security checklist for vibe-coded apps
Before you launch an app built with AI tools, check five things in this order: who can read each table (as a visitor and as a second signed-up user), that no secret key is in your code or git history, that roles and admin checks live in the database rather than the browser, that every server function checks who is calling, and that your files and dependencies aren’t exposing anything. This checklist links each item to a self-check and a fix.
TL;DR
- Data first: test each table with personal data as a logged-out visitor and as a stranger who signed up.
- Keys: no secret keys in the code, the bundle or git history. Rotate any that ever were.
- Auth: roles in
app_metadataor a roles table, email confirmation on, admin rules enforced by the database. - Server code, storage and dependencies: functions verify callers, webhooks verify signatures, private files in private buckets,
npm auditclean of serious advisories.
How to use this list
Work top to bottom: the early items expose the most data. Each item says what “done” looks like and links to a guide with the exact commands. It is written for apps built with Lovable, Bolt, Replit or Cursor on a Supabase backend. If your stack is Firebase, swap section 1 for Firebase rules basics.
Two principles behind every item: anything in the browser is visible and editable by the user, and a rule you haven’t tested as a second user is a rule you don’t know works.
1. Data access
- Row level security is on for every table in
public. Query in Step 1 of the database guide. (PL-008) - No table with personal data is readable by a logged-out visitor. Test with the anon role. (PL-001)
- No table with personal data is readable by a stranger who signed up. This is the one most often missed:
to authenticated using (true)looks safe and isn’t. (PL-002) - Strangers can’t change or delete other people’s rows, and update policies have
WITH CHECK. (PL-012, PL-004) - Views run as the caller (
security_invoker = true) or expose only public columns. (guide) - Tables published to Realtime have tight read policies. (guide)
- All of it is in
supabase/migrations, not only in the dashboard, so it can be rebuilt and re-tested.
How to test: the three-identity script in RLS policies explained.
2. Keys and secrets
- No Supabase service role or secret key anywhere in frontend code or in a
VITE_/NEXT_PUBLIC_variable. (PL-010) - No AI, payment or database secret in the code. They live in server-side secrets. (PL-009)
- No secret in git history. Any that ever were are rotated. (PL-011, guide)
- The built bundle is clean:
npm run build, then searchdist/forsk_live_,sk-proj-,sk-ant-,service_role. .envfiles are ignored, with a.env.examplecommitted instead.
3. Sign-in, roles and admin
- Email confirmation is on in your project’s authentication settings. (PL-018)
- Roles are not in
user_metadata. Useapp_metadataor a roles table. (PL-019, guide) - Users can’t write their own role: no insert policy on the roles table for users, no role column in what users may update. (PL-013)
- Admin pages are backed by admin-only database rules, not just a route guard. (PL-020)
- No access decided by email domain or a hard-coded email address.
4. Server code
- Every Edge Function verifies its caller with
getUser(), a signature or a shared secret. (PL-017, guide) - No function takes a user id from the request body. (PL-022)
- Payment webhooks verify signatures over the raw body. (PL-021, guide)
- Cookie-authenticated endpoints don’t reflect any origin with credentials. (PL-026)
security definerfunctions haveset search_path = ''and aren’t executable byanonunless meant to be. (PL-006)
5. Files, frontend and dependencies
- Personal files are in private buckets with per-user folder policies, shown through signed URLs. (PL-007, PL-015, guide)
- User content isn’t rendered as raw HTML, or is sanitised first. (PL-024)
npm audit --omit=devshows no high or critical advisories you haven’t looked at. (PL-023, guide)
6. Run Supabase’s Security Advisor
In the Supabase dashboard, open the Security Advisor and clear what it reports. It catches several items above (RLS off, mutable function search paths, owner-rights views, policies that read user_metadata). It deliberately doesn’t judge public reads, because only you know which tables are meant to be public, which is why section 1 asks you to test as a stranger yourself.
7. After launch
- Re-check after every round of AI changes. One prompt can rewrite a policy.
- Know your first hour. Read what to do in the first hour after a leak now, not during one.
- Give security reports somewhere to go: a contact address on your site.
Sources
Related findings
The checks behind this guide, each with example code, a self-check and the fix.
Anonymous visitor can read personal data
PL-002Any signed-in user can read every row
PL-008Row level security not enabled
PL-009AI, payment or database key in the code
PL-010Service role key in client code
PL-011Key still in git history
PL-017Edge function open without sign-in while using the service key
PL-019Access decided by user_metadata, which users can edit
PL-021Payment webhook doesn't verify its signature
PL-023Dependency with a known vulnerability
Free check
Check your app for this, free.
Paste your public repo. PatchLabs rebuilds your database from its migrations, tests it as a visitor, a stranger and the owner, reads the code for keys and risky patterns, and hands you the fix.
Written by Manraj Singh Dhillon. Last checked 10 Nov 2026. Code samples are synthetic and run against your own project; nothing here names a real app. Spotted a mistake? Tell us.