Supabase RLS check for Lovable, Bolt and v0 apps: a 2-minute self-test
Needs verification
Every app that talks to Supabase from the browser ships its project URL and anon/publishable key. That's normal. But it means any table without correct Row Level Security (RLS) policies can be read, and sometimes written, by anyone on the internet using that key.
Why it matters
This is the issue behind CVE-2025-48757, where a researcher reported missing or insufficient RLS across 170 Lovable-built projects. Unspill never queries your tables (that would require testing your database without your dashboard access). Instead, run these checks yourself in the Supabase SQL editor.
Quick fix
- Open Supabase → SQL Editor and run the RLS status query from the guide.
- Enable RLS on every table in the public schema.
- Replace any policy that uses USING (true) on private data with one scoped to auth.uid().
- Open Supabase → Advisors → Security Advisor and fix every error it lists.
Step by step
1. Find tables without RLS
Run this in the Supabase SQL Editor. Any row with rowsecurity = false is readable by anyone holding your anon key (unless you've revoked grants).
select schemaname, tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;2. Turn RLS on
Enabling RLS with no policies blocks all access through the API, so add policies right after.
alter table public.profiles enable row level security;3. Find policies that let everyone in
Policies with qual = 'true' (USING (true)) allow every row. That's fine for genuinely public data (e.g. published blog posts) and dangerous for anything personal.
select tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename;4. Scope rows to their owner
The typical fix for user-owned data:
create policy "Users read own rows" on public.profiles
for select to authenticated using ((select auth.uid()) = user_id);
create policy "Users update own rows" on public.profiles
for update to authenticated using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);5. Check Storage and views
Storage buckets marked Public serve every file to anyone with the URL. Views run with the owner's rights by default; in Postgres 15+ create them with (security_invoker = true) so RLS applies. Supabase's Security Advisor flags both.
6. Test as a stranger
In a private browser window, logged out, open your app and the browser devtools Network tab. Any Supabase /rest/v1/ response that returns other people's data is a leak.
Prompt for your AI builder
Paste this into Lovable, Bolt, v0, Cursor or Replit, then review the changes before deploying.
Audit my Supabase schema for Row Level Security. For every table in the public schema: enable RLS, and write explicit policies so that users can only select/insert/update/delete rows where user_id = auth.uid() (or the correct ownership column). Do not use USING (true) for any table containing personal or private data. Make storage buckets private unless they only hold public assets. Output a single SQL migration and explain each policy in one line.FAQ
Is it a problem that my Supabase anon key is visible?
No. Supabase designs the anon/publishable key to be used in browsers. The protection comes from Row Level Security policies on each table.
Why doesn't Unspill test my tables automatically?
Some scanners do, by querying your database with your public key. Unspill stays strictly passive: it only reads the files your site already serves, so it is safe to run against any production app, and it never touches your users' data. The SQL above gives you a definitive answer from inside your project.
Does Lovable/Supabase warn me about this?
Supabase has a built-in Security Advisor (Dashboard → Advisors) that flags tables with RLS disabled and other risky settings. Run it after every schema change.
References
- Supabase docs: Row Level Security
- Supabase docs: Database Advisors
- Matt Palmer: CVE-2025-48757 (missing RLS in Lovable projects)