Critical · AI-built app security

An exposed database (row-level security)

In one paragraph

Supabase (the database most AI builders use) is only safe when row-level security — RLS — is enabled with real policies. By default, tables ship with RLS off, so the public anon key in your frontend lets anyone with your project URL read, change, or delete every row. Worse, a table can show RLS "enabled" with a policy of USING (true), which looks safe and blocks nothing. The fix is RLS on every table, with policies scoped to who should actually see each row.

180findings in this category across our live scans of real AI-built apps · see the data

Your Supabase anon key is meant to be public — but only because RLS is supposed to stand between it and your data. When RLS is off, that key is a skeleton key to the whole database. This is the most damaging issue in vibe-coded apps because it exposes user data directly, and it's invisible in a working app: everything looks fine until someone queries the API themselves.

Why it happens

RLS is off by default

New Supabase tables start with row-level security disabled. The app the AI builds works perfectly against them — the protection is simply never turned on.

Policies that block nothing

A policy of USING (true) satisfies "does a policy exist?" while allowing every request to read every row. Some builder scans check for a policy's existence, not whether it actually restricts anything.

New tables slip through

Add a feature later and the new table it creates may ship without RLS, even if your original tables were locked down.

Check it yourself

  • In the Supabase dashboard, every table with user data should show the green "RLS enabled" badge.
  • Log out of your app, then try to read your data with the anon key — if rows come back, RLS isn't protecting them.
  • Review each policy: USING (true) on a table with real user data means it's open.

How to fix it

  • Enable RLS on every table that holds user or private data.
  • Write policies that scope rows to the owner — e.g. the authenticated user's ID must match the row's user_id.
  • Re-run the check every time you add a table or feature, since new tables can ship unprotected.

Not sure if your app has this?

SafeLaunch checks your repo and live site in about 30 seconds — this issue and every other — and gives you a prompt to paste back into your builder to fix each finding.

Scan my app · $19

Most common in apps built with

Exposed database (RLS) FAQ

What is row-level security in Supabase?+

RLS is the rule layer that decides which rows each request can see. With it off, your public anon key exposes the whole table; with it on and scoped properly, that same key only returns the data a given user is allowed to read.

Is my Supabase anon key supposed to be public?+

Yes — the anon key is designed to live in the frontend. It's only safe when RLS is enabled, because RLS is what stops that key from reading everything.

My table shows RLS enabled but data still leaks — why?+

Almost always a policy of USING (true), which is technically a policy but allows every request. Replace it with one that scopes rows to the right user.

Other issues