VibeSecurity

Incidents and lessons

CVE-2025-48757: What the Lovable RLS Flaw Teaches Supabase Apps

CVE-2025-48757 is the identifier assigned in 2025 to a vulnerability class: apps generated with Lovable whose Supabase databases had missing or weak Row Level Security, so anyone holding the public key could read or write tables. The record is contested, and the argument over who is responsible is instructive in itself. What matters for you is the mechanism, because it applies to every Supabase project, whichever tool wrote it.

By the VibeSecurity team6 min read

What does CVE-2025-48757 actually say?

The National Vulnerability Database entry was published on May 30, 2025. It describes an insufficient database Row Level Security policy in Lovable, through April 15, 2025, that allows remote unauthenticated attackers to read or write arbitrary database tables of generated sites.

The same entry carries a dispute note. The supplier's position, as recorded there, is that each customer of the platform accepts responsibility for protecting the data of their own application. The NVD entry is tagged as disputed and, at the time of writing, its status is deferred, which means it has not been fully analyzed.

That framing is worth holding onto. The recorded disagreement is about responsibility: whether this is a product defect or a customer configuration issue, and for a builder that distinction changes nothing about your risk: if your tables are open, your users' data is open.

What was reportedly found in the wild?

The researcher who disclosed the issue published findings, which the NVD entry links to. Secondary coverage reported that a public scan flagged 170 of 1,645 showcased projects as having exposed data. We have not independently reproduced that scan, so treat the figure as reported, not verified, and do not read it as a measurement of all Lovable apps or of AI-built apps generally.

The scale of any single number matters less than what it shows: a showcase of apps that looked finished and worked in a browser could still have tables that an anonymous request could read. Working software and safe software are different properties, and preview builds only test the first one.

How does the exposure work technically?

Supabase exposes your Postgres database through an auto-generated REST API. A browser app calls that API directly, using your project URL and a public key. Both values ship inside your JavaScript bundle, by design. Supabase documents its publishable key as safe to expose in web pages, mobile apps and source code, because it is meant to work together with Row Level Security.

That last clause carries the whole security model. The public key does not grant privileges. It identifies the request as coming from the anon role, and Postgres then decides what that role may touch. If RLS is off on a table in an exposed schema, there is nothing to decide: the request is allowed. If RLS is on with a policy such as using (true), the decision is also yes.

The NVD wording covers both read and write. A table that is readable by anon leaks data. A table that is writable by anon lets a stranger change prices, roles or ownership fields, which is often worse.

Why do AI-generated apps hit this so often?

An assistant is optimizing for a working feature. It creates a table, writes a query, and the page renders. In the preview you are usually signed in as yourself, so everything you can see looks correct. Nothing on screen reveals that a stranger without a login can see the same rows.

Two other habits compound it. Generated migrations sometimes create tables without ever enabling RLS, and quick fixes for a permission error can loosen a policy to make the error disappear. The app then works again, and the protection is gone.

What does it teach every Supabase app?

  • Treat the public key as public. Rotating it does not fix a table with RLS off, because the new key is just as visible.
  • Enable RLS on every table in every exposed schema, including tables created later by a generator.
  • Enabling RLS with no policies denies access through the API. That is the safe default; add the narrowest policy that makes a feature work.
  • Write policies for the operation they govern. Reads, inserts, updates and deletes each need their own reasoning.
  • Never use the secret or service_role key in browser code. It bypasses RLS entirely, per Supabase's own documentation.

How do I test my own project?

Only run this against a project you own. Start by asking the database itself which public tables have RLS switched off. Then list the policies that are effectively unconditional.

SQL editor: find tables with RLS off
select schemaname, tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;

Step two: look for policies that allow everything

A table can have RLS on and still be open. This query shows policies whose conditions are literally true, which behave like no protection for that operation.

SQL editor: find unconditional policies
select tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public'
  and (qual = 'true' or with_check = 'true')
order by tablename;

Step three: test as an outsider would

Finally, confirm from the outside. Send a request with only the public key, no user session, to a table you own. Then try a write against a disposable row. A permission error or an empty result on reads, and a rejected write, is what you want.

Terminal: anonymous read and write attempt
curl "https://<project-ref>.supabase.co/rest/v1/<table>?select=*&limit=1" \
  -H "apikey: <public-key>" \
  -H "Authorization: Bearer <public-key>"

curl -X POST "https://<project-ref>.supabase.co/rest/v1/<table>" \
  -H "apikey: <public-key>" \
  -H "Authorization: Bearer <public-key>" \
  -H "Content-Type: application/json" \
  -d '{"<column>": "test"}'

What should I do if a test shows exposure?

  1. 1Enable RLS on the affected table immediately. With no policies, API access is denied, which stops the bleeding while you design real policies.
  2. 2Write a narrow policy per operation and re-run the outsider test.
  3. 3Review your data for changes you did not make. Anonymous write access means you cannot trust rows, not just that they were read.
  4. 4If the table held personal data, check the notification duties that apply to you and speak to a qualified lawyer. This post is not legal advice.
  5. 5Add the two SQL checks to your release routine so a new table cannot ship unnoticed.

Frequently asked questions

Is CVE-2025-48757 still exploitable today?

The CVE describes a class of misconfiguration in generated projects, not a single patchable bug in your code. Whether a given app is exposed depends on its own tables and policies. The only reliable answer is to test your own project with the SQL checks and an anonymous request.

Is the Supabase anon key a secret?

No. Supabase documents its publishable key as safe to expose in browsers, apps and source code. It is designed to be paired with Row Level Security. The secret or service_role key is the one that must never leave a server, because it bypasses RLS.

Does turning on RLS break my app?

It can, and that is expected. With RLS on and no policies, the API denies access, so features that relied on open tables stop working until you add policies. Add the narrowest policy for each operation, then test as both a signed-in user and an anonymous visitor.

Was Lovable found to be at fault?

The NVD entry records that the supplier disputes the CVE, arguing customers are responsible for protecting their own application data. This post does not settle that question. It treats the issue as a misconfiguration pattern that any Supabase project can have.

Put it into practice

Sources

  1. 1.NVD: CVE-2025-48757
  2. 2.Supabase docs: Row Level Security
  3. 3.Supabase docs: API keys
  4. 4.PostgreSQL docs: CREATE POLICY