Almost every dangerous Supabase finding lives at the database boundary — the row-level-security policies, the keys, and what the client is trusted to send. Here's what we see most.
Critical
Row-level security off — or on with an open policy
The single most common critical finding. A new table ships with RLS off; the quick fix is a USING (true) policy that turns the dashboard green while letting the public anon key read every row. We confirm each table has RLS on and that every policy is actually scoped to the user.
check › Database → Policies: any policy with USING (true) on a readable table is effectively public.
Critical
The service_role key in client-side code
The anon key is meant to be public; the service_role key bypasses RLS entirely. If it has leaked into the frontend bundle or an unguarded edge function, the whole database is open no matter how good your policies are.
check › Search your repo and built bundle for service_role / SERVICE_ROLE_KEY.
Critical
Only SELECT is locked — insert, update, delete are open
A common half-fix: a read policy scoped to the user, but no policy on insert, update or delete. Each command needs its own policy, or anyone can write or wipe rows even when reads look secure.
check › Confirm separate policies exist for insert, update and delete — not just select.
Watch
Multi-tenant data scoped by a client-sent id
When org_id or team_id is taken from the request instead of derived from the session, one tenant can read another’s data by changing a value. We trace where the boundary is actually enforced.
check › Is org_id / team_id ever read from the client and trusted directly in a query?
Watch
Public storage buckets and unguarded edge functions
Storage buckets left public and edge functions that don’t verify the caller are easy to miss — files and privileged actions exposed to anyone who has the URL.
check › Review bucket policies and confirm each edge function checks the caller.
These are the patterns, not a checklist run against your repo. The audit reads your
actual Supabase code and tells you which of these are really there — prioritized,
critical first. Or try the free 2-minute self-check →