← All posts
·10 min read·GuardLayer

Lovable RLS vulnerability (CVE-2025-48757) and the 2026 leaks

LovableSupabaseRLSCVEAI

CVE-2025-48757 is what happens when an AI builds a working app on top of a Supabase database it never locks down. In May 2025, security researcher Matt Palmer disclosed that Lovable-generated apps were shipping with missing or insufficient Supabase Row Level Security — so anyone holding the public anon key (the one that ships in every browser bundle by design) could read, and in many cases write, tables full of user data. His analysis found 303 endpoints across 170 production Lovable apps leaking this way. No login, no exploit chain. The fix is one line of SQL per table — and you can check your own app in about a minute.

It didn't stay at 170. In November 2026 a much larger study found email addresses readable on 7,403 Lovable apps (more on that below), and the same kind of mistake is the likely cause.

If you've shipped anything with Lovable, Bolt, v0, or Cursor on top of Supabase, this is the single most important thing to verify before you call the app "done" — and it's item one on the vibe-coded SaaS security checklist.

What is CVE-2025-48757?

CVE-2025-48757 is a critical vulnerability class in apps generated by Lovable, the AI app builder, when paired with a Supabase backend. The official CVE record describes it plainly: "An insufficient database Row-Level Security policy in Lovable through 2025-04-15 allows remote unauthenticated attackers to read or write to arbitrary database tables of generated sites."

The CVE record on NVD carries a CVSS 3.1 base score of 9.3 (Critical), vector AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N — network-reachable, no privileges required, no user interaction. Note the I:L: this isn't a read-only leak. The same hole that lets an attacker pull rows also lets them write to tables with no policy blocking it.

Two honest caveats, because this is a security brand and the details matter. The 9.3 is the CNA (MITRE) score — at time of writing NVD had not published its own re-enriched assessment, so treat the 9.3 as the assigning authority's number, not an independent NVD rescore. And the CVE is formally marked "Disputed": Lovable's position is that each customer is responsible for securing their own application's data. That dispute is, in a sense, the whole point. The platform generates the app, but you own the security boundary, the same way you would for code a junior wrote at 2am. There's no patch to install — the mitigation is something you do to your own database.

How did 170 apps leak the same way?

The root cause is structural, not a few sloppy users — and it comes down to how Supabase exposes data. Every table in the public schema is reachable through an auto-generated PostgREST API, and the only credential needed to hit it is the anon key — which is designed to be public and ships in your client-side bundle, along with your project URL. Neither is a secret. The single thing standing between an anonymous visitor and the rows in a table is Row Level Security.

When you create a table without RLS — or with a policy that doesn't actually scope rows to the right user — that table becomes a public API endpoint. Anyone can run this from a laptop:

curl "https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*" \
  -H "apikey: YOUR_ANON_KEY"

And get back every row.

Here's why the AI builder walks straight into it: Lovable, like every tool of its kind, optimizes for "it works in the preview." It generates a schema with create table, wires up a polished frontend, the dashboard query returns rows, and the demo looks perfect. A missing RLS policy is invisible in that demo — the app behaves identically whether the table is locked or wide open. The security boundary is the one part of the build that produces no visible result, so it's the first thing left undone.

Matt Palmer's analysis put a number on it: 303 endpoints across 170 production Lovable apps — roughly 10% of the projects he scanned — exposing data to unauthenticated requests, including emails, names, and third-party API keys and access tokens the apps had stored. Just the documented Supabase API doing exactly what it was told, against tables nobody locked.

The November 2026 study: 7,403 apps

Eighteen months later the same pattern showed up at a far larger scale. The Hamburg security firm Crissy Field went through Common Crawl and found 159,041 Lovable projects published between December 2025 and September 2026. Of the 34,545 that use Supabase, 24,664 showed database records on page load with no login. Some of that is meant to be public, so on its own it isn't a leak. The sharper finding: with read-only requests using the same public key, the researchers found email addresses on 7,403 sites, across 10,560 tables. Reported examples include a forum exposing more than 80,000 profiles and 200,000 chat messages, a provider with over 1,000 identity records including passport numbers, and one project with 280,000 customer emails (the report, Techzine's write-up). Not every address is private, and misuse hasn't been shown. The report blames access-control mistakes without naming a setting, but on Supabase that kind of exposure means what this CVE describes: tables with RLS off, policies that let anyone read, or a view or function that skips RLS.

This is the same failure mode behind the broader wave of AI-built app data leaks — CVE-2025-48757 is simply the best-documented instance of it.

Note that the CVE covers "insufficient" RLS, which is really two distinct failures. An app leaks either because RLS was never enabled on a table the model created, or because the AI generated a policy that looks like access control — typically using (true) — but doesn't actually filter rows by user. That second one is the using (true) trap, and it leaks just as badly as no policy at all.

The one-line-per-table fix

You don't disable anything, and you don't wait for a vendor patch. You enable RLS and write the policy that grants the access your app genuinely needs.

-- Keep the gate ON for every table in public.
alter table public.profiles enable row level security;

-- Grant exactly what the app needed: a user sees only their own row.
create policy "Users read own profile"
  on public.profiles
  for select
  to authenticated
  using ( (select auth.uid()) = user_id );

auth.uid() returns the authenticated user's UUID and null for the anon role, so anonymous callers fail the comparison and get nothing back. Wrapping it as (select auth.uid()) lets Postgres evaluate it once per query instead of once per row — Supabase's own recommended pattern for RLS performance, with no change to the security behavior.

If a table is genuinely meant to be world-readable (a public blog_posts table, say), still keep RLS on and write an explicit read-only policy — for select ... using (true) to anon, authenticated. That documents the intent and still blocks the anonymous writes the I:L in the CVE vector is about. The difference from a disabled table is enormous: this grants SELECT only, on purpose, while a disabled table grants INSERT, UPDATE, and DELETE too, by accident.

A 60-second self-check on your own app

Run this before you tell anyone your Lovable app is finished.

1. Ask Postgres which public tables have RLS off right now. Paste this into the Supabase SQL editor:

select tablename
from pg_tables
where schemaname = 'public'
  and not rowsecurity;

Anything this returns is reachable by anon this second. If you didn't intend that table to be public, it's exposed.

2. Test it like an attacker would. Grab your project URL and anon key from Supabase settings, pick a table that should be private, and try to read it with nothing but the public key:

curl "https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*" \
  -H "apikey: YOUR_ANON_KEY"

If you get rows back without logging in, that table is leaking to the entire internet. An empty array or a permission error is what a properly locked table looks like.

3. Audit your policies, not just the RLS flag. A table can have RLS on and still leak via a using (true) policy, or one that only checks that the caller is signed in (auth.uid() is not null, to authenticated with using (true)). If anyone can sign up, or anonymous sign-ins are on, "signed in" means anyone. List your policies and confirm each filters by auth.uid() where it should:

select tablename, policyname, qual
from pg_policies
where schemaname = 'public';

Catch this before you push, not after

The self-check above is a great one-time audit. The problem is that your next AI prompt — "add a comments feature" — creates a new table, and you're back to square one without realizing it. The hole is silent by definition: no error, no failing test, no deploy warning.

That's the gap GuardLayer closes. It statically scans your Next.js + Supabase code and migrations on every push — flagging tables created without RLS, disable row level security statements, using (true) policies, and service-role keys exposed to the client — and posts the findings on your pull request before the change can merge.

For the live database, connect your Supabase project read-only: GuardLayer reads RLS settings, policies, grants and your Auth settings together (never your rows), so it can tell when a policy that looks fine is public in practice because anyone can sign up. Every finding comes with the SQL fix and a prompt to paste back into Lovable, and "Check again" confirms what's closed. If your project only syncs to GitHub, scan the repo instead. More on both in the Lovable security check.

To be straight about scope: GuardLayer never sends traffic to your deployed app. The repo scan reads code and migrations; the live check reads your database's structure through Supabase's read-only endpoint.

FAQ

Is the code Lovable generated secure? The frontend usually works fine. The risk is the database boundary: Lovable creates Supabase tables and a polished UI, but whether each table has a correctly scoped Row Level Security policy is on you. CVE-2025-48757 found 303 endpoints across 170 Lovable apps leaking because that policy was missing or too loose. Run the self-check above before trusting any AI-built Supabase app.

What exactly is CVE-2025-48757? A critical vulnerability class (CVSS 9.3, the CNA/MITRE score) in Lovable-generated apps through 2025-04-15, where insufficient Supabase RLS let unauthenticated attackers read or write arbitrary database tables using only the public anon key. Disclosed by Matt Palmer in May 2025. It is formally "Disputed" by the vendor on shared-responsibility grounds.

Isn't the anon key secret? How would anyone reach my tables? No. The anon key is public by design and ships in your browser bundle, along with your project URL. RLS is the protection layer — not the key. With RLS off or misconfigured, the anon key is all an attacker needs. There's no patch to wait for: the fix is enabling RLS on every public table and scoping policies with using ( (select auth.uid()) = user_id ).

Does enabling RLS break my app? No — it changes "everyone can read this table" to "only the right user can." If a query starts returning empty after you enable RLS, that's the signal you were missing a policy. Add a scoped one rather than disabling RLS to make the error go away.

How many Lovable apps have leaked data? CVE-2025-48757 (May 2025) covered 170 apps. A study first reported on November 2, 2026 found 24,664 Lovable sites showing database records without a login and email addresses readable on 7,403 of them. Neither was a breach of Lovable or Supabase: the data was reachable because of how each app's database access was configured.

I already shipped a leaking table. What now? Enable RLS immediately, add the correct policies, and treat the data as exposed for the entire window it was open. Rotate any third-party API keys that were stored in exposed tables, and check your Supabase logs for anomalous PostgREST traffic against that table.

See which of your Supabase tables anyone can read, free

Connect your Supabase project read-only. GuardLayer checks RLS, policies, grants and functions as they are right now, and gives you the SQL to fix each problem. It never reads your rows and can't change anything.

Check my Supabase project

Prefer to check code instead? Scan your repo, no signup.

Keep reading