Check your row level security
Paste your schema. See every table anyone can read, every policy that lets everyone through, and the SQL that closes each one.
What it checks
- RLS off
- Tables in the public schema that never enable row level security, and tables whose policies are ignored because of it.
- Open policies
- Policies that pass every row: USING (true), WITH CHECK (true), no expression at all, or a check that only asks "is someone signed in".
- Forgeable checks
- Policies that trust user_metadata, which a signed-in user can edit from the client.
- Policies that do nothing
- INSERT policies with a USING clause (Postgres rejects them) and UPDATE or DELETE policies with no SELECT policy to find the row.
- Views
- Views without security_invoker, which run with their owner's rights and skip the policies on the tables they read.
- Speed
- Policy columns with no index, and auth.uid() calls that run once per row instead of once per query.
Stop it at the migration
On Scribase, every migration runs through a lock-safety and RLS lint before it applies — a table shipped without Row Level Security is refused, not merely flagged. $25 a month flat, Supabase-compatible.
Questions people ask
Is my schema uploaded anywhere?
No. The SQL is parsed by JavaScript on this page. No request is sent and nothing is stored, so you can check a production schema.
How do I get my schema as SQL?
Run pg_dump --schema-only against your database, or supabase db dump -f schema.sql with the Supabase CLI. You can also paste your migration files in order. A full dump with data works too; the data rows are skipped.
My policies looked fine in the SQL editor. Why are they flagged?
The Supabase SQL editor runs as the postgres role, which bypasses row level security, so every query there succeeds whatever the policies say. Your app runs as anon or authenticated, where the policies apply.
Is an UPDATE policy without WITH CHECK a hole?
Not by itself. When WITH CHECK is left out, Postgres checks the new row with the USING expression. The real hole is WITH CHECK (true) next to a strict USING: the owner can then rewrite the row into anything, including handing it to another user. The checker flags that case as exposed and the missing WITH CHECK only as a tip.
What does it not see?
It reads the SQL you paste, not your live database. It does not see GRANT and REVOKE statements, policies created outside the pasted SQL, or what your application code does. RLS-off is reported for tables in the public schema; policy checks run on every schema, including storage.objects.