Supabase RLS Not Working (or Disabled)? Fix It Before Someone Finds It
Why ~70% of Lovable apps ship with row-level security disabled, what CVE-2025-48757 means for your app, and how to enable RLS without breaking everything.
If your app was built with Lovable, Bolt, or another AI builder on top of Supabase, there's a very good chance your database is readable — possibly writable — by anyone on the internet right now.
This isn't scaremongering. Research behind CVE-2025-48757 (CVSS 8.26, High) found that roughly 70% of sampled Lovable-built apps had row-level security disabled entirely, with 303 exposed endpoints confirmed across 170 projects — leaking everything from emails to payment data. The AI builds a working demo; it does not secure your tables.
How to check if you're exposed (2 minutes)
- Open your Supabase Dashboard → Table Editor. Any table with an "RLS disabled" warning badge is publicly accessible through your project's auto-generated REST API.
- Confirm it from the outside — this is exactly what attackers do:
curl "https://YOUR-PROJECT.supabase.co/rest/v1/users?select=*" \
-H "apikey: YOUR_ANON_KEY"
Your anon key is not a secret — it ships in your frontend JavaScript to every visitor. If that curl returns rows, so can anyone.
Why "just enabling RLS" breaks your app
Enabling RLS with no policies denies everything — and suddenly your app shows empty lists and silent failures. This is why the AI (or a previous developer) probably turned it off in the first place: the app "worked" without it. RLS needs to be enabled and given correct policies:
-- Enable RLS on the table
alter table public.orders enable row level security;
-- Users can only see their own orders
create policy "Users read own orders"
on public.orders for select
using (auth.uid() = user_id);
-- Users can only insert orders for themselves
create policy "Users insert own orders"
on public.orders for insert
with check (auth.uid() = user_id);
Every table needs this thinking applied: who may read which rows, who may write which rows, and what your server-side code (using the service role) does on behalf of users.
The other landmine: the service role key
The service_role key bypasses RLS entirely. It must only ever live in server-side environment variables. AI-generated code regularly embeds it in frontend code or commits it to the repo — at that point, RLS doesn't matter, the attacker has god mode. Search your codebase for service_role and rotate the key immediately if it appears anywhere a browser can see.
The safe migration path
- Inventory every table and what the frontend actually queries.
- Write policies first, enable RLS table by table, testing each app flow as you go.
- Move privileged operations (webhooks, admin actions) to server-side code using the service role — never the client.
- Verify from outside with curl against the REST API using only the anon key.
- Rotate keys if the service role key was ever exposed.
When to bring me in
If your app is live with real user data and RLS off, you have both a security emergency and a delicate migration — enabling policies wrong takes your app down. My $299 triage audits every table, policy, and key exposure in 48 hours and gives you a prioritized fix plan; the Priority Fixes package implements it without breaking your production app.
Stuck? I'll fix it for a fixed price.
I'm a senior full-stack engineer who audits, fixes, hardens, and deploys broken Lovable, Bolt, Cursor, v0, and Replit apps. Fixed price — diagnose before I touch a line of code, so you stop burning credits on prompts that make it worse.
Vibe-Code Rescue — from $299