Not a warning about your rules. A test of who can read your data.
Lint tools tell you a policy looks permissive. PatchLabs Lab rebuilds your database from its own migrations, queries it as an anonymous visitor, a signed-in stranger and the owner, and shows you the rows each one got.
| public.profiles · select | anon | stranger | owner |
|---|---|---|---|
| Beforeas your migrations built it | 2 rowsLEAK | 2 rowsLEAK | own rowOK |
| Afterwith the fix applied | 0 rowsDENIED | 0 rowsDENIED | 1 rowOK |
-- 1. anonymous visitor, with your public key
set local role anon;
select count(*) from public.profiles; -- 2: LEAK
-- 2. signed-in stranger
set local role authenticated;
select set_config('request.jwt.claims', '{"sub":"<stranger>"}', true);
select count(*) from public.profiles; -- 2: LEAK
How it works
From migrations to a reproduced leak, in a lab we throw away.
Every Lab finding is something we made happen, not something we guessed from reading SQL.
Rebuild
Your
supabase/migrationsrun in a disposable Postgres with Supabase’s roles, auth functions and default grants.migrations applied roles: anon, authenticatedSeed
Fake rows for two test users in every table, so there is something to read and something to protect.
2 users · fake rows no real dataProbe
Query each table as an anonymous visitor (your public key), a signed-in stranger and the owner.
anon → 2 LEAK stranger → 2 LEAK owner → 1 OKReport
Each leak becomes a finding with an ID, the columns exposed, who can read them and the evidence.
PL-001 · critical public.profiles
Capabilities
8 Lab checks live today.
Each links to its finding page: the vulnerable pattern, how to check it yourself and the fix we write.
Anonymous visitor can read personal data
A read policy for anon or public on a table with emails, phones or addresses, not scoped to the user.
Any signed-in user can read every row
Signing up unlocks other people’s rows because the policy checks only that someone is signed in.
RLS enabled but no policies (owner locked out)
Row level security is on with no policies, so nobody, including the owner, can read the table.
UPDATE policy missing WITH CHECK
Users can edit a row they own into a row they shouldn’t, such as handing it to someone else.
Always-true policy
A policy written as using (true) or with check (true) that applies to everyone it names.
SECURITY DEFINER function exposed
A function that runs with its creator’s rights is callable by anon or public, or has no search_path.
Public storage bucket
A bucket created with public = true, so anyone with a file link can download it.
Row level security not enabled
A table in the public schema with RLS never switched on: the public key reads and writes it.
Coming to Lab
Announced, not live. Each will appear in reports only once it ships and is tested. Until then they are not counted.
Strangers can change or delete other people’s rows
Write access reproduced as a signed-in stranger: their UPDATE or DELETE lands on someone else’s row.
Users can make themselves admin
A role or is_admin column the user can update on their own profile.
View skips row level security
A view that runs with its owner’s rights, so the table’s policies never apply.
Stored files readable or writable by anyone
Policies on storage.objects that let strangers list, download or overwrite files.
Realtime sends private rows to subscribers
A table published to realtime whose changes reach subscribers who can’t read it.
PL-019 (access decided by user_metadata) is live today as a Code check, which reads it from your code and SQL. Reproducing it in the lab comes later.
Sample output
What a Lab finding looks like.
The transcript from our demo app, then the finding it produced. Personal values are redacted before anything is stored.
- Finding
- PL-001 · Anonymous visitor can read personal data
- Object
public.profiles·email,phone- Proof
- anon read 2 rows, signed-in stranger read 2 rows
- Severity
- critical
- Next
- PatchLabs Fix writes and proves the migration
[REBUILD] 1 migration → lab Postgres OK Supabase roles, auth, default grants [SEED] 2 test users, fake rows OK [PROBE] as anon (your public key) select * from public.profiles; → 2 rows readable LEAK email •••••@••••• phone +•• •••• •••• [PROBE] as stranger → 2 rows LEAK [PROBE] as owner → own row OK [FINDING] PL-001 · critical · public.profiles
Why behaviour, not lint
A rule can look fine and still leak.
- Read-only public tables show as intended, not as noise. If anyone may read your shop listings, mark them once and later checks remember.
- Policies combine. Two harmless-looking policies can add up to “anyone can read”. The lab sees the result, not the parts.
- Grants matter. We rebuild with Supabase’s default grants, so a table without RLS behaves exactly as it would in your project.
- Owners must still work. Every probe includes the owner, so a “fix” that locks real users out is caught.
What the lab can’t see. It is only as complete as the migrations in your repo. Changes made by hand in the Supabase dashboard, or a backend other than Supabase/Postgres, are outside it, and the report says what couldn’t be rebuilt. It is not a pentest or a compliance certification.
Do you connect to my Supabase project?
No. We never need your database password or service key. The lab is a local Postgres rebuilt from the migrations in your public GitHub repo, seeded with fake rows and deleted after the run.
What if my migrations don’t build?
The report says which migration failed and what we couldn’t test. In our study, 318 of 436 repos rebuilt to a working database; the rest were mostly missing or out-of-order migrations.
How is this different from Supabase’s Security Advisor?
The advisor lints your live project’s settings, and it’s worth running. The lab tests behaviour: it signs in as each identity and counts the rows they get. Full comparison.
Are the five coming checks in my report?
No. PL-012 to PL-016 are announced so you know what’s next. They’ll appear in reports once they ship and are tested, and the changelog will say so.
Free check
Can a stranger read your users table? Find out in minutes.
Paste your public repo. You’ll get the leaks, the fix and the proof on one page.