LEAKCADE

Supabase RLS mistakes that leak your data

Your Supabase anon key ships in your frontend. That's by design. What keeps strangers out of your tables is Row Level Security (RLS), and AI-built apps often get it wrong.

Why this matters

Supabase's own docs put it bluntly: a table in an exposed schema without RLS is readable and writable by any role that has a grant on it. In March 2025, researcher Matt Palmer scanned 1,645 apps built with Lovable and found 303 exposed endpoints across 170 of them, about 10.3%. The issue became CVE-2025-48757. Lovable disputes the CVE, saying each customer is responsible for protecting their own app's data. Either way, the data in those tables was reachable.

If a stranger can open your app, they have your anon key. RLS and your grants are what stand between that key and every row in your tables.

Mistake 1: tables created in SQL without RLS

Tables you make in the Supabase Table Editor get RLS on by default. Tables created with SQL, in the SQL editor or in a migration file, don't. If your AI tool wrote the schema as a migration file, check it. Check every create table in supabase/migrations for a matching line:

alter table public.todos enable row level security;

Mistake 2: policies that say using (true) on writes

RLS is on, but the policy lets anyone update or delete any row. for update to authenticated using (true) still means every signed-up user can edit every row. Scope writes to the owner:

create policy "own rows: update" on public.todos
for update to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);

Mistake 3: forgetting grants

Policies don't remove grants. Supabase's docs note that on existing projects a new table in public starts with select, insert, update and delete granted to anon and authenticated. Revoke what signed-out visitors don't need: revoke all on table public.todos from anon;

Mistake 4: the service role key in the browser

The service role (secret) key bypasses RLS completely. If it ends up in client code or a NEXT_PUBLIC_ variable, your policies don't matter. Keep it on the server only and rotate it if it ever shipped.

Fix prompt you can paste

Go through every table in supabase/migrations. For each one: make sure RLS is
enabled, replace any policy that uses (true) for insert/update/delete with an
ownership check on auth.uid(), revoke grants anon doesn't need, and write a new
migration with the changes. List any table that should stay public read-only.

How Leakcade checks this

Our auth-route check reads your SQL migrations as text. It flags tables that are created without enable row level security and policies that allow writes with (true). It can't see changes made only in the dashboard, so the report tells you when that's the likely explanation.

When we tested 13 public repos, this check flagged 12 open write policies in one Supabase blog starter and one table without RLS in another.

Further reading

Sources

  1. Supabase docs: Row Level Security
  2. Matt Palmer: Statement on CVE-2025-48757
  3. NVD: CVE-2025-48757

Want a second pair of eyes?

We review your whole repo and write a fix prompt for every finding. $19, report within an hour.

Check my repo

Leakcade is an automated check. It can miss vulnerabilities and is not a substitute for a professional security audit or pentest. Provided as is. You stay responsible for your code. Where the law allows, our liability is limited to what you paid ($19). Terms

Patch Notes by Leakcade

A short weekly email: what changed in AI coding tools, one real security mistake, one prompt you can reuse.