Guide · Lovable · Supabase · Updated
CVE-2025-48757 explained: what happened to Lovable apps, and how to check yours
If you build with Lovable, you have probably seen this CVE number come up in a thread or a reply and felt your stomach drop a little. We wanted one page that explains it without the drama: what actually happened, why it happens, how to check your own app in a few minutes, and what has changed since. Everything here links to the record, the researcher’s own write-up, or the vendor docs, so you can check our reading of them.
The short version
CVE-2025-48757 says that insufficient Row Level Security in Lovable, through 15 April 2025, let remote attackers who were not signed in read or write arbitrary database tables of the sites it generated. The CVE record classes it as CWE-863, Incorrect Authorization, and lists a CVSS 3.1 score of 9.3, critical. It also carries a note that Lovable disputes it, because each customer of the platform accepts responsibility for protecting their own app’s data.
In plain words: some Lovable apps let any stranger ask the database for everything, and the database said yes. Nobody had to guess a password. The key they used was the public one that every visitor’s browser already has. What was missing was the rule that should have said “only your own rows.”
What happened
The issue was found by Matt Palmer, who works in developer relations at Replit, with his colleague Kody Low. Replit also makes an AI app builder, which is worth knowing as you read. His CVE write-up and longer statement lay out the timeline, and this is our summary of it.
20 March 2025
Palmer looks at the network requests of Linkable, a Lovable-built site that made websites from LinkedIn profiles. Changing one query gives him the whole users table. In his first email to Lovable he says it exposed the emails of about 500 people.
21 March 2025
He and his colleague Kody Low write a script that opens the homepage of every project on Lovable Launched, the showcase of finished Lovable apps, and asks each database endpoint it sees for all of its rows. It finds 303 open endpoints across 170 of the 1,645 projects, about 10.3%, from homepages alone. They email Lovable the same day.
24 March 2025
Lovable confirms it received the email.
14 April 2025
Another engineer, Danial Asaria, independently posts on X that he got into several of Lovable’s “top launched” sites in 47 minutes and pulled out personal debt amounts, home addresses, and API keys. Palmer re-notifies Lovable and starts a 45-day disclosure window.
Late April 2025
Lovable 2.0 ships. Its changelog entry, dated 25 April, adds a Security Scan that “checks for vulnerabilities when you publish (if you’re connected to Supabase).” Palmer’s view is that this scan only checked that some RLS policy existed, not whether it was correct.
24 May 2025
Palmer re-tests Linkable. Signed-in requests now look protected, but dropping the sign-in header turns the request into a signed-out one, and that path still reads everything. He inserts a record with payment_status set to paid, which skips Stripe entirely.
29–30 May 2025
Palmer publishes his write-up and statement on 29 May. The CVE record is published on 30 May.
The examples in the statement are what make this real. Among the open endpoints were tables called users, transactions and subscriptions, and endpoints with names like get-google-maps-token, get_gemini_api_key and refresh-ebay-token. Palmer also points out that his script only ever looked at homepages. It never signed in, so whatever sat behind a login was not counted.
The payment_status detail is the one that stays with us. It means the problem was not only that strangers could read. On a table with a missing rule, a stranger could also write, and mark their own order as paid.
Why it happens
Palmer’s write-up describes the setup in one sentence: Lovable makes front ends that call the database’s REST API directly from the browser, using a public and unprivileged anon key, “while relying exclusively on RLS” to keep the data private. That is a normal, documented way to use Supabase. It just means one layer is doing all the work.
The key is public by design. Supabase’s API keys guide says anything you ship to a browser uses the publishable key (the anon key on older projects), and that anyone can read it, so it only reaches what Row Level Security allows. Their picture of it is a good one: the publishable key is taped to the front door and only opens the lobby. The secret key, the old service_role key, is the master key, and it stays in your pocket. Palmer said the same about his own first reaction: the core issue was not an exposed key, since Supabase hands out public anon keys on purpose, but the absent RLS.
RLS is the actual lock. Row Level Security is a set of rules inside Postgres that decide which rows each request may see or change. Supabase’s RLS docs are blunt about the other case: a table in an exposed schema without RLS is readable and writable by any role with a grant on it. And on existing projects, a new table in public starts out granting select, insert, update and delete to the signed-out anon role. Adding policies doesn’t take those grants back.
There are a few common ways an app ends up with a door open:
- RLS was never turned on for a table. Supabase’s API security guide says tables created in the dashboard have RLS on by default, but tables created in the SQL Editor or by another tool need it turned on by hand. Tables written by an AI in a migration fall in the second group.
- RLS is on, but the rule lets everyone through. A policy like
using (true)for signed-out visitors means every row, for everyone. Palmer calls this out by name as a thing to avoid on sensitive tables. - The rule covers signed-in users, but the signed-out path is still open. That is what the May re-test of Linkable found. The app looked protected when you used it normally, because the app always sent a signed-in request. A stranger simply didn’t.
- Private and public columns share a row. RLS works on whole rows. Palmer notes that if a row mixes a username with payment details, anyone allowed to see the row sees both, so sensitive data belongs in its own table.
None of this needs a clever attacker. Palmer describes the method: open the app, watch the network traffic, and change the request to ask for all rows, for example with ?select=*. That is also why a scanner can find it, and why you can too.
Was my app affected?
The record covers Lovable through 15 April 2025. Palmer’s write-up says it affects Lovable projects with a database created on or before that date, “but may also affect projects created after that date.” Honestly, we wouldn’t spend long on the date. The useful question is whether any of your tables are open right now, and that takes about fifteen minutes to answer.
Everything below is read-only. Nothing writes to your database. Please only run it against an app you own.
- Find the key and the requests your page sends
- Ask each table for one row, as a stranger
- Run a read-only audit query
- Try a second account
- Run Lovable’s own scan, and read it with care
01
Find the key and the requests your page sends
Open your live app in Chrome, open DevTools, go to the Network panel, reload, and type rest/v1 in the filter box. Each row is your page talking straight to the database. Click one and note two things from the request: the project URL (it looks like https://something.supabase.co) and the apikey header. Write down the table names you see in the paths too.
If the key starts with sb_publishable_, it is the public one and that is expected. If it starts with sb_secret_, or it is an eyJ key whose role turns out to be service_role, that is a different and worse problem. Check 2 of our Lovable guide shows how to tell which one you have, without pasting the key into anyone’s website.
02
Ask each table for one row, as a stranger
From a terminal on your own machine, ask for a single row the way any visitor to your site could. This is a plain read with a limit of one, the same kind of request Palmer describes, minus the “give me everything.”
curl "https://YOUR-PROJECT.supabase.co/rest/v1/YOUR_TABLE?select=*&limit=1" \
-H "apikey: YOUR_PUBLIC_KEY"- Real data from a table that should be private: this is the CVE pattern. Anyone on the internet can read that table today. Go to the fix below.
- An empty list,
[]: either RLS filtered everything out for a stranger, or the table is empty. Good, as long as you know which. - A permission error (code
42501): the signed-out role has no grant on the table at all. Also good, if that is what you meant.
Do this for every table that holds anything about your users. Profiles, orders, messages, leads, invoices. The one you forgot about is usually the one that matters.
03
Run a read-only audit query
The terminal test only checks tables you know about. This query asks Postgres itself about every table in public. It only reads the catalog, so it changes nothing. In Supabase, paste it into the SQL Editor. We tested it against a local Postgres with a table for each case below.
-- Read-only: this changes nothing. Paste it into the Supabase SQL Editor.
-- One row per table in public: is RLS on, what a signed-out visitor may try,
-- and which policies let signed-out visitors through with no condition at all.
select c.relname as table_name,
c.relrowsecurity as rls_on,
has_table_privilege('anon', c.oid, 'select') as anon_may_read,
has_table_privilege('anon', c.oid, 'insert,update,delete') as anon_may_write,
(select string_agg(p.policyname || ' (' || p.cmd || ')', ', ')
from pg_policies p
where p.schemaname = 'public'
and p.tablename = c.relname
and p.roles && array['anon', 'public']::name[]
and (p.qual = 'true' or p.with_check = 'true')) as open_to_anyone
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public'
and c.relkind in ('r', 'p')
order by c.relrowsecurity, c.relname;How to read the result:
rls_onis false andanon_may_readis true: this is exactly the situation in the CVE. Anyone with your public key can read the whole table, and ifanon_may_writeis also true, change it.open_to_anyonenames a policy: RLS is on, but that policy lets every signed-out visitor through with no condition. That is fine for data you would put on a billboard, like published blog posts. It is not fine for anything else.rls_onis true andopen_to_anyoneis empty: no unconditional door for strangers. The grants may still be wider than you need, which the RLS checklist covers, but this is the result you want.
If you would rather click, the Security Advisor in the Supabase dashboard has lints for exactly these, including “RLS disabled in public”, “policy exists RLS disabled” and “RLS enabled no policy.” On Lovable Cloud, read your rules under More → Cloud → Database → RLS policies.
04
Try a second account
Make two test accounts. Sign in as the second and try to open something that belongs to the first, for example by putting the first account’s record ID in a URL. You should see nothing of theirs. Palmer recommends this test too: try to reach data you shouldn’t, both signed out and as one user reaching for another user’s data. It catches a rule that is on but too loose, which the other checks can miss.
05
Run Lovable’s own scan, and read it with care
In your project, open More → Security. Lovable’s security docs say the Quick scan runs every time you publish and flags things like tables without per-record access control and rules that let everyone through. The Deep scan reads your code for users who can see data that is not theirs. Lovable says both scans are free to run.
A clean result is a good sign. It is not proof, and Lovable says so itself: the scans “do not replace a thorough security review.” A scanner can see that a rule exists. It can’t know that your leads table was never meant to be public. Palmer’s advice from 2025 still holds: review your policies regularly, even if the security scanner doesn’t show any issues.
How to fix it
The fix is the same whatever tool wrote your app, and it is mostly small. Palmer’s list of mitigations comes down to this: turn RLS on for every table, write narrow policies tied to the signed-in user with auth.uid(), avoid using (true) on anything sensitive, check what the signed-out role is granted, keep secrets out of tables the browser can reach, and move sensitive columns into their own table.
We wrote that up step by step, with tested SQL for each part, in the Supabase RLS checklist. Rather than repeat it here, start there with the tables your audit flagged. If you would like Lovable to do the first pass, its security best practices page suggests this prompt: “Review RLS policies to ensure users can only access their own data and shared data is properly protected.” Then run checks 2 to 4 again yourself.
One thing worth adding: if a table that held API keys or tokens was open, assume those keys are out. Fixing the rule closes the door from now on. It doesn’t un-copy anything. Revoke those keys at the service that issued them and make new ones.
What has changed since
Quite a lot, on both sides. From Lovable’s changelog, blog, and docs:
- April 2025: the publish-time Security Scan in Lovable 2.0, described in the changelog as “just the beginning of our work to make vibe coding safer by default.”
- June 2025: a post on secure vibe coding says Lovable integrated Supabase’s Security Advisor into that scan, added an AI-powered Security Reviewer for issues the scan misses, and was by then blocking around 1,200 private API keys a day from being pasted into code. The same post says: “While we can’t guarantee your app will be 100% secure, this feature should catch the most common problems.”
- October 2025: Lovable Cloud, a built-in backend that Lovable’s docs say uses Supabase’s open-source foundation. So the same rules apply, and Cloud has its own view of your RLS policies.
- June 2026 onward: two scan levels, now called the Quick scan and the Deep scan. The Quick scan runs on every publish. Lovable can also auto-fix common RLS misconfigurations while you build, if you turn that on. In August 2026 it added a Trust Center page for published apps, which it describes as informational, not a certification.
And from Supabase:
- The API keys guide says Supabase is deprecating the old anon and service_role keys by the end of 2026, in favour of publishable (
sb_publishable_) and secret (sb_secret_) keys. - The API security guide says Supabase is changing the platform default to stop granting new tables to
anonandauthenticatedautomatically, so that exposure becomes opt-in.
What hasn’t changed is the shape of the thing. Apps like these can still talk to the database straight from the browser, and the rules are still the only lock. Lovable’s docs still say you are responsible for your app meeting the security your use case needs. And the CVE record still carries its dispute note. Better tools make the open door rarer and easier to spot. They don’t make it impossible, especially for the table someone added in a hurry last week.
Where LockShip fits
We built LockShip because check 2 is easy to forget and boring to repeat after every change. You paste your app’s URL and prove you own the domain. The scan then looks at your app the way a stranger would. It finds the Supabase URL and public key in your page, sees which tables that key can list, and makes read-only requests for at most one row from each. It also looks for secret keys in your JavaScript, tries /.env and /.git, and reads your security headers.
It is just as important to say what it doesn’t do. It never signs in as your users, so it can’t do the second-account test. It doesn’t read your policies from the inside the way the audit query does, and it never writes to your database. It is a point-in-time, best-effort check, not a penetration test. A clean result means “nothing obvious from the outside right now,” not “safe.” Use it as one option next to the checks above, not instead of them.
The summary is free: a score and the single worst thing we found. The $39 Launch Safe certificate adds the full report with fix prompts for your builder, the RLS SQL, a PDF, and seven days of re-scans while you fix things. A passing re-scan gives you a badge for your site.
Want a second pair of eyes on check 2?
Paste your app’s URL, prove you own the domain, and see what a stranger can read. The summary is free.
Run a free scanBefore you rely on a result, read the disclaimer.
Questions people ask
Is CVE-2025-48757 a bug in Supabase?
No. The CVE record names Lovable as the affected product. Supabase’s Row Level Security does what its docs say it does. The problem described in the CVE is apps that were generated with missing or too-loose RLS policies, so the lock was never set.
Is the Supabase key I can see in my page the vulnerability?
No. The public key (publishable, or the older anon key) is meant to be in your page. Supabase’s docs say anyone can read it and that it only reaches what Row Level Security allows. Palmer said the same thing in his statement: the real issue was not the public key but the missing RLS behind it. The key that must never be in a page is the secret or service_role key.
Is there a patch I need to install?
No. There is nothing to update on your side. The weakness lives in each app’s own database rules, so the fix is per app: turn RLS on for every table and write policies that match who should see what. Lovable has added scanning and auto-fix tools since 2025, but they work on your project, they don’t change it for you behind the scenes.
My app was built after 15 April 2025. Am I safe?
Not automatically. The CVE record covers Lovable through 15 April 2025, but Palmer’s write-up says it may also affect projects created after that date. Any app whose browser code talks to the database directly can have the same problem if one table is missing a rule. The date matters less than what your tables look like today.
Why does Lovable dispute the CVE?
The record’s own note says the supplier disputes it because each customer of the Lovable platform accepts responsibility for protecting the data of their application. Lovable’s current security docs say the same in other words: you are responsible for making sure your app meets the security it needs.
I use Lovable Cloud instead of my own Supabase project. Does this apply to me?
Yes. Lovable says Cloud is built on Supabase’s open-source foundation, and its data access is controlled by row-level security policies, which you can read under More → Cloud → Database → RLS policies. The checks on this page are the same.
Keep reading
- A Supabase RLS checklist for vibe-coded apps
- Is my Lovable app secure? Seven checks you can do yourself
Sources
We checked each of these on 16 October 2026. The timeline and numbers come from Palmer’s write-up and statement. Lovable and Supabase both change their products often, so if something here disagrees with their docs, trust the docs and tell us at hi@lockship.dev.
- CVE-2025-48757, NVD
- CVE-2025-48757, CVE record
- CVE-2025-48757 write-up, Matt Palmer
- Statement on CVE-2025-48757, Matt Palmer
- Post on Lovable “top launched” sites, Danial Asaria on X
- Changelog, Lovable docs
- Lovable: Secure Vibe Coding, Lovable blog
- Security overview, Lovable docs
- Security best practices for Lovable apps, Lovable docs
- Lovable Cloud, Lovable docs
- API keys, Supabase docs
- Row Level Security, Supabase docs
- Securing your API, Supabase docs
- Database advisors, Supabase docs