Supabase Row Level Security is off: your whole database is readable with the anon key
Symptoms
- The Supabase dashboard shows 'RLS disabled' warnings you may never have looked at
- Anyone who copies the Supabase URL and anon key out of your app's JavaScript (both are always visible in the bundle) can read, modify, or delete every row through the auto-generated REST API, no login needed
- The Security Advisor lists tables with RLS disabled
- Your app was generated by an AI builder and you never wrote a single policy
Why it happens
AI codegen skipped RLS entirely
The AI ran CREATE TABLE statements without `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` and without any policies. The tables work, the app works, and the database is wide open.
The anon key is public by design
Supabase's anon key is meant to be shipped to browsers. It is only safe when RLS restricts which rows each request can touch. With RLS off, the anon key is effectively an admin key.
The CVE-2025-48757 pattern
This exact failure exposed 170+ production Lovable-built apps, and it sat behind Moltbook's leak of roughly 1.5 million API tokens and tens of thousands of emails. Research on the Lovable disclosure found about 10% of analyzed apps with RLS bypassed. Lovable patched its generation pipeline, but every app generated before the fix stayed vulnerable until its owner enabled RLS manually.
The service_role variant
A related failure is shipping the service_role key in client code. That key bypasses RLS entirely, so enabling RLS does nothing against it.
How to fix it
- List every exposed table (do this now, your data is publicly readable until RLS is on)In the Supabase dashboard, open Advisors / Security Advisor, or scan the Table Editor, and list every table with RLS disabled. Each one is fully readable and writable with the anon key from your bundle.
- Enable RLS on each tableRun `ALTER TABLE public.your_table ENABLE ROW LEVEL SECURITY;` for each exposed table. Enabling RLS with zero policies blocks all anon access, so parts of your app will break until you add policies. That is the safe direction to break in.
- Write explicit policies per operationFor example: `CREATE POLICY "own rows" ON public.profiles FOR SELECT USING ((SELECT auth.uid()) = user_id);` and write equivalents for INSERT, UPDATE, and DELETE. Each operation gets its own deliberate policy.
- Check for a leaked service_role keySearch your frontend bundle for the service_role key. If it ever shipped client-side, rotate it in Project Settings > API immediately. RLS does not protect against service_role.
- Verify from the outsideWith no auth, run `curl 'https://<project>.supabase.co/rest/v1/<table>?select=*' -H 'apikey: <anon-key>'` and confirm you get only the rows an anonymous user should see, which is usually none.
How to prevent it
- Enable RLS the moment a table is created; make it part of every CREATE TABLE the AI runs.
- Check the Security Advisor after every AI-driven schema change.
- Keep the service_role key server-side only, forever.
- Re-run the anonymous curl test against your tables after each schema migration.
Tools that actually fix this
Recommended because they address the failure mode above, not because of the payout. Some are affiliate links; see how we choose.
If your AI-built app uses Supabase, Row Level Security is the actual fix for exposed data, and it is free. The dashboard's Security Advisor lists every table you left open.
Try Supabase →Drop-in authentication that handles sessions, tokens, and protected routes properly, so you stop shipping the hand-rolled auth an AI guessed at. Generous free tier for a real app.
Try Clerk →Frequently asked questions
Is the Supabase anon key safe to expose in my frontend?
The Supabase anon key is designed to be public, but only when Row Level Security is enabled with policies on every table. With RLS off, the anon key works like an admin key: anyone who copies it from your bundle can read, modify, or delete every row through the auto-generated REST API. Enable RLS and write per-operation policies before treating the anon key as safe.
What is CVE-2025-48757?
CVE-2025-48757 is the vulnerability pattern where Lovable-built apps shipped with Supabase Row Level Security disabled or bypassed, leaving their databases readable and writable with just the public anon key. It exposed 170+ production apps, and research found roughly 10% of analyzed apps affected. Lovable patched its generation pipeline, but apps generated before the fix stayed vulnerable until owners enabled RLS manually.
Will enabling RLS break my app?
Probably, and that is by design. Enabling RLS with zero policies blocks all anonymous access, so queries your app relied on will start returning nothing until you add explicit policies for SELECT, INSERT, UPDATE, and DELETE. Breaking closed is the safe direction: better a broken feature than a public database.
What if the service_role key was in my client code?
Rotate it immediately in Project Settings > API. The service_role key bypasses Row Level Security entirely, so enabling RLS does nothing against it. Treat any service_role key that ever shipped in a bundle as compromised, and use it only in server-side code afterward.
Sources
- Supabase Row Level Security guide (Vibe App Scanner)
- Post-mortem: Lovable CVE-2025-48757 (vibecoder.me)
- Lovable security vulnerabilities (Ship Safe)
- Common security risks in vibe-coded apps (Wiz Research)
Related errors
Lovable Supabase RLS not enabled: anyone can read your app's data (CVE-2025-48757)
'new row violates row-level security policy' in Supabase (403 / error 42501)
The AI put your API key in the frontend: OpenAI, Stripe, or Supabase keys visible in the JS bundle
No auth on your AI-generated API endpoints: anyone can call your backend
Bolt.new + Supabase: 403 Forbidden, data not saving, or the wrong project connected
The Vibe Oops briefing
One email when something ships to production that should not have. New incidents, new error guides, no filler.