Guide · 4 min read
Checking Supabase row-level security: is your app leaking data?
Apps built with Lovable, Bolt and many other AI tools store their data in Supabase. Whether strangers can read that data comes down to one setting per table: row-level security. This guide shows you how to check it yourself.
A Supabase app talks to the database straight from the browser. For that, a public key sits in the frontend – the anon or newer publishable key. That’s by design. It’s only safe, however, if the database itself decides who may see which row. That’s what row-level security (RLS) does.
If a table has no RLS, anyone with the public key can read its entire content – and often change it. In Lovable projects this was so common that it got its own security advisory: CVE-2025-48757, severity 9.3 out of 10 (Lovable disputes the rating).
Step 1: Open the Security Advisor
In the Supabase dashboard under Advisors → Security Advisor. Supabase checks your database automatically. Pay particular attention to these warnings:
- RLS Disabled in Public – a table in the public schema has no row-level security. This is the critical case.
- RLS Enabled No Policy – RLS is on but there’s no policy. Nobody sees anything – safe, but often a sign the app works differently than intended.
- Security Definer View – a view runs with its creator’s rights and thereby bypasses RLS.
Every warning at level “Error” should be fixed before going live.
Step 2: Query it yourself like a stranger
You’ll find the public key and project URL in the project settings under API – or in your app’s network requests in the browser. Then query a table without being logged in:
curl 'https://YOUR-PROJECT.supabase.co/rest/v1/customers?select=*' \
-H "apikey: YOUR_PUBLIC_KEY"
Read the result like this:
| Response | Meaning |
|---|---|
[] |
Good: RLS is active and gives anonymous users nothing. |
| Data | Critical: the table is publicly readable. |
| “permission denied” error | Good: the role has no access at all. |
Repeat this for every table with personal data, orders, messages or payment information.
Step 3: Test two accounts against each other
Empty responses for anonymous users aren’t enough. The more common flaw is an overly generous policy for logged-in users – such as using (true) or a policy that only checks whether someone is logged in.
- Create two test accounts, A and B.
- Create a few records with account A.
- Log in as B and fetch the data – in the app, or with
curlusing B’s access token in theAuthorization: Bearer …header.
If B can see A’s data, the policy is too open.
Step 4: Set the policies correctly
A typical, safe policy for data that belongs to a user:
alter table public.notes enable row level security;
create policy "Read own notes"
on public.notes for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Create own notes"
on public.notes for insert
to authenticated
with check ((select auth.uid()) = user_id);
Both parts matter: using governs what someone may see and change, with check what they may write. Without with check, a user can create records in someone else’s name. For business apps with several users per customer, the policy checks membership of the organisation instead of the user ID.
What else stands out
- Service key in the frontend. The
service_roleorsecretkey bypasses RLS completely. It must never be in browser code. If it is, rotate it in the dashboard immediately. - Views and functions. Views should be created with
security_invoker = true. Database functions markedsecurity definerrun with admin rights and need their own checks. - Storage. File uploads have their own policies on
storage.objects. A public bucket makes every file available to anyone who knows the address. - Edge Functions. Check that every function verifies the caller’s token before working with elevated rights.
If you found something
Assume the data could already have been accessed. Close the gap, rotate affected keys, and check with an expert whether you have a duty to report – under Swiss law to the FDPIC if there is a high risk, under the GDPR to your supervisory authority.
And because RLS is rarely the only issue: the 10 most common security flaws in AI code covers the other candidates.