Security

Supabase RLS for Lovable Apps: Seven Mistakes, Each With a Test

Published Updated 5 min readBy Vibe Coding Intel editorial

Researched from official docs and public sources, checked .

The short answer: if your Lovable app uses Supabase, the public key in your front end can reach any table in an exposed schema, and only Row Level Security (RLS) decides which rows come back. Supabase’s docs state it plainly: a table in an exposed schema without RLS “is readable and writable by any role with a grant on it”. So the check that matters is one you run yourself: call your own database with the public key, signed out, and confirm that every table that holds user data returns nothing. The seven mistakes below each come with that kind of test.

How the test works

Supabase maps every request to a Postgres role: anon for signed-out visitors and authenticated for signed-in users. The public (publishable or anon) key in your app’s code is meant to be seen, so assume anyone can use it. Grants decide whether a role can touch a table at all; policies decide which rows (Supabase RLS docs).

For any table, replace the placeholders and run it from a terminal, using only the public key:

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

An empty list or a permission error such as “permission denied for table” is a pass for a private table. Rows are a fail. An error about an invalid key or JWT is not a pass: it means the request never reached the table, so fix the request and run it again (a newer publishable key goes in the apikey header only; a legacy anon key also works there). Then repeat while signed in as user A, asking for user B’s rows. This is a manual check, not a scanner; it proves only the tables you test.

Seven mistakes

#MistakeWhat the docs sayTest that should fail
1RLS never enabled on a tableWithout it the table is open to any role with a grantSigned-out request returns rows
2RLS on, no policies, then “fixed” with a broad oneWith RLS on and no policies, client requests using the public key get nothing until you add policiesNot applicable: the symptom is an empty app, and the tempting fix is mistake 3
3A policy that is always true (using (true))A policy decides which rows an operation affects; always-true makes all rows eligibleSigned-in user A can read user B’s rows
4Policy not scoped to the signed-in roleSupabase’s examples add to authenticated so signed-out (anon) requests never match the policySigned-out request returns rows
5Policies written for select onlyWrite separate policies for select, insert, update and delete; insert uses with check, update uses bothSigned-in user A can update or delete user B’s row
6A view over a protected tableViews bypass the underlying table’s RLS by default; on Postgres 15 and later, create the view with security_invoker = true to enforce itQuery the view signed out
7The service_role key in front-end codeservice_role bypasses RLS entirely and belongs on the server onlySearch your repository and built JavaScript for the key

Supabase’s securing your API page says grants decide which roles can reach tables, views and functions, and that new objects in the public schema get default grants for anon, authenticated and service_role. It shows how to revoke those defaults so exposure is opt-in. Review which roles may call each function, because a row policy does not filter what a function does.

A policy pattern from the docs

For a table with a user_id column, the shape Supabase documents is a per-operation policy that checks the signed-in user, for example for reading:

alter table public.notes enable row level security;

create policy "own rows, read" on public.notes
  for select to authenticated
  using ( (select auth.uid()) = user_id );

Supabase wraps auth.uid() in select so Postgres evaluates it once per statement rather than per row. Add matching insert (with check), update (using and with check) and delete (using) policies. Enabling RLS on a table that already has data locks client access until a policy exists, so add the policies in the same change. This is the documented pattern with example names; adapt it, run the tests above and do not treat it as a drop-in answer for tables with teams, roles or shared rows.

What Lovable does and does not do

Lovable’s security docs describe a Quick Scan that runs when you publish (database and dependencies) and a Deep Scan, which you start yourself from the Security view, that reviews application code for access control, unauthenticated endpoints and leaked secrets, plus a Security view where you inspect and fix findings. It flags common problems such as tables without per-record access control. That is a reviewer, not a proof: our Lovable security checklist covers where its own documentation says scans do not guarantee safety, and the production readiness checklist sequences the launch checks. If you are choosing between Lovable Cloud and your own project, see Lovable Cloud vs Supabase.

The CVE people cite

The National Vulnerability Database lists CVE-2025-48757, published 30 May 2025. It describes an insufficient database RLS policy in Lovable through 15 April 2025 that lets unauthenticated remote attackers read or write arbitrary tables of generated sites. The record carries a CVSS 3.1 score of 9.3 from the MITRE source, and a note that the supplier disputes it, because each customer of the platform is responsible for protecting their own application’s data. The entry’s status is “Deferred”. Read it as a documented class of failure in apps generated before that date, with the vendor and the reporter disagreeing about who owns the fix, and treat the practical lesson as ownership: check your own tables.

Test before you launch

  1. List every table and mark which hold user data.
  2. For each, run the signed-out test, then the cross-user test with two real accounts.
  3. Search the repository and the deployed JavaScript for service_role.
  4. Re-run after any change to tables, policies or auth.

If a test fails and you cannot tell why, that is the point to bring in someone who writes SQL, because a policy you do not understand is a guess.

Method note

Researched from Supabase’s and Lovable’s official documentation and the NVD record; checked 2 October 2026. The seven-item list is ours, built from Supabase’s documented rules. The commands were not run against a live project for this page, and no breach statistics are quoted because the secondary figures we found trace to vendor reports rather than primary studies.

Sources

Update log

  1. New page. Row Level Security rules read in Supabase's RLS and Data API security docs; Lovable's scanner behaviour from its security docs; the CVE entry read from the NVD record.