Michael Bires
← All fix-it guides

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)

  1. 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.
  2. 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

  1. Inventory every table and what the frontend actually queries.
  2. Write policies first, enable RLS table by table, testing each app flow as you go.
  3. Move privileged operations (webhooks, admin actions) to server-side code using the service role — never the client.
  4. Verify from outside with curl against the REST API using only the anon key.
  5. 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