CVE-2025-48757: is your Lovable app exposing data?

By the RowLocked team · Updated

In May 2025, a vulnerability was published as CVE-2025-48757 describing how many apps generated with Lovable exposed their users' data through Supabase. The issue was not a bug in one line of code. It was a pattern: apps whose database tables had missing or overly broad Row Level Security (RLS) policies, reachable directly with the public key every visitor already has.

If you have a Lovable app with a database, this guide explains what was reported, why it happened, and how to check and fix your own app. The same applies to apps built with Bolt, Cursor, v0 or any other tool on top of Supabase.

What was reported

According to the published advisory by Matt Palmer, the vulnerability concerns insufficient RLS policies in Lovable-generated projects that use a database. The advisory says it affects projects created on or before 15 April 2025, but may also affect projects created after that date. It describes how an unauthenticated attacker could send crafted requests directly to the project's database endpoints to read data, and in some cases insert, update or delete it.

The kinds of data the advisory lists as exposed include personal information from user tables (names, email addresses), API keys and tokens for third-party services stored in tables, and financial records such as transactions and subscription status, including the ability to modify payment status in some cases.

The researchers' statement also criticized the security scanner Lovable introduced at the time for checking only whether an RLS policy existed, not whether it was correct. Coverage at the time, including by Semafor, reported on the scale of affected apps featured on Lovable's site. Lovable has since added more security tooling, but older projects and later edits can still carry the original problem.

Why it happened

Lovable apps are mostly client-side. The React frontend talks directly to Supabase using the project URL and the anon (publishable) key, both of which are embedded in the JavaScript bundle. That is a normal, supported Supabase architecture, and it is safe only when Row Level Security on every table enforces who can see what.

The failures fell into a few groups:

  • RLS disabled on some tables, so the public key could read and write everything in them.
  • RLS enabled with policies that allowed everyone, such as using (true), often added to make a page load.
  • Policies that did not match the app's logic. The UI only showed a user their own orders, but the database policy allowed reading all orders. The frontend filter looked like security but was not.
  • Secrets stored in tables the public key could read, such as third-party API tokens kept in a settings table.

An attacker does not need your app's UI at all. They copy the URL and key from the bundle and query the REST API directly:

GET https://<project>.supabase.co/rest/v1/<table>?select=*
apikey: <public anon key>

Am I affected?

Your Lovable app may be affected if it uses Supabase and any table holds data that should not be public. Age matters less than you might think: the advisory notes that later projects may be affected too, and every prompt that adds a table or "fixes" a permissions error can introduce the same pattern.

Check these four things:

1. The Security Advisor. In your Supabase dashboard, open Advisors, then Security Advisor. Errors such as *RLS Disabled in Public*, *Security Definer View* and *Policy Exists RLS Disabled* are direct signs. Warnings about permissive policies deserve a look too.

2. Every table's RLS status and policies:

select c.relname as table_name, c.relrowsecurity as rls_on,
       coalesce(string_agg(p.policyname || ' [' || p.cmd || ']: ' || coalesce(p.qual, p.with_check, ''), E'\n'), '(no policies)') as policies
from pg_class c
join pg_namespace n on n.oid = c.relnamespace and n.nspname = 'public'
left join pg_policies p on p.schemaname = 'public' and p.tablename = c.relname
where c.relkind = 'r'
group by c.relname, c.relrowsecurity
order by c.relrowsecurity, c.relname;

Any table with rls_on = false, or with a policy whose expression is just true on personal or financial data, needs fixing.

3. What the public key can actually read. The SQL Editor bypasses RLS, so test from outside with the key from your app:

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

Repeat for each sensitive table. Private tables should return [] or an error. RowLocked's free scan automates this for the tables it can discover from your app, using count-only requests so no rows are downloaded or stored.

4. Secrets in tables. Search for columns that look like credentials:

select table_name, column_name
from information_schema.columns
where table_schema = 'public'
  and column_name ~* '(key|token|secret|password)'
order by table_name;

Any third-party secret stored in a table the browser can read should be moved to Edge Function secrets and rotated.

How to fix it

Enable RLS everywhere in the public schema and write one narrow policy per operation. For a typical user-owned table:

alter table public.orders enable row level security;

create policy "orders: buyer can read own" on public.orders
  for select to authenticated
  using ((select auth.uid()) = user_id);

-- No insert/update/delete policies: orders are written by server code
-- (a payment webhook in an Edge Function using the secret key).

Notice that this example deliberately gives the browser no write access. Payment status should be set by your payment provider's webhook running on the server, never by client code. That alone closes the "modify payment status" problem described in the advisory.

Step-by-step fixes for the common cases are in Supabase "RLS Disabled in Public", and copy-paste patterns for profiles, teams, public content and admin access are in Supabase RLS policy examples.

Move secrets out of the database. Store third-party keys as Edge Function secrets and call those services from functions, not from the browser. Rotate any key that was ever in a readable table.

Fix views and functions. Set security_invoker = true on views in public, and review security definer functions. See security definer views.

Ask Lovable to do a pass, then verify. A prompt you can paste:

Audit every Supabase table this app uses. Enable Row Level Security on all of them. For each table, write one policy per operation that matches who should have access: users only see and change their own rows via auth.uid(), public content is read-only to everyone, and anything related to payments, roles or credits is written only by server-side Edge Functions. Remove any "using (true)" policy on user data. Move any API keys stored in tables into Edge Function secrets. List every table with its final policies in plain English.

Then re-run the checks above. AI-generated policies are a good start but should be verified against what your app is supposed to allow.

If you find exposed data

If a sensitive table was readable or writable by the public, fixing RLS stops future access but does not undo past access. Review the Supabase API logs for unusual bulk reads, check for unexpected or modified rows, rotate any secrets that were stored in exposed tables, and consider whether you need to notify users under the privacy laws that apply to you.

Keep checking

RLS problems are not a one-time fix. Every new feature can add a table, and every "it doesn't load, fix it" prompt is a chance for a policy to be loosened. Re-run the Security Advisor after significant changes, and check your live app from the outside regularly. RowLocked is built around exactly that: a free scan now, and weekly re-checks that tell you when something changes.

Frequently asked questions

Is CVE-2025-48757 a bug in Supabase?

No. Supabase's RLS works as designed. The advisory concerns how Lovable-generated projects configured (or failed to configure) RLS policies.

My app was created after April 2025. Am I safe?

Not automatically. The advisory itself says later projects may be affected, and the underlying mistake, missing or overly broad RLS, can be introduced by any edit. Run the checks above.

Does Lovable's built-in security scan cover this?

Use it, but verify the results yourself. The researchers specifically criticized the original scanner for checking only that policies existed, not that they were correct. Testing with the public key from outside your app shows what an attacker would actually see.

Is the anon key in my Lovable app a leak?

No. It is meant to be public. The problem is tables that the anon key can read or write because RLS is missing or too broad. See Is the Supabase anon key safe to expose?

Related guides

All guides

Check my app now