Home / RLS / Fix a permissive USING (true) policy

Supabase RLS · Fix

USING (true) is a security hole — how to fix it

Postgres 15 · Supabase✓ code tested against a real database
TL;DR

USING (true) means 'everyone, always' — it quietly makes the table public read. Find them with a query on pg_policies and replace each with a policy scoped to (select auth.uid()) or workspace membership.

01 Why USING (true) leaks

An RLS policy's USING clause is the filter for which rows a caller may see. USING (true) evaluates true for every row and every caller — so anyone with your anon key can read the whole table. It's the second most common Supabase leak, right after RLS being off, and it usually gets copy-pasted in from a tutorial that just wanted the demo to work.

02 Find every permissive policy

audit.sql

select schemaname, tablename, policyname, cmd, qual
from pg_policies
where schemaname = 'public'
  and qual = 'true';      -- USING (true)

Also check with_check = 'true' for permissive writes. Anything that comes back is a table any user can read (or write).

03 Replace it with a scoped policy

migration.sql

drop policy "public read" on notes;

create policy "owner reads own notes" on notes for select
  using (user_id = (select auth.uid()));

04 If you actually want public read

Sometimes public read is the point (a blog, a public profile). Do it on purpose and scope the columns — don't expose the whole row. Restrict to a published flag and select only safe columns:

migration.sql

create policy "anyone reads published posts" on posts for select
  using (is_published = true);

That still isn't USING (true) — it's a deliberate rule. Keep private columns out of any table that has a public policy.

◆ FREE · MIT · npm + GitHub Action

Catch this before it ships

airlock-rls is a CI gate that fails your build when a table ships exposed or a policy is permissive — the same class of bug, caught on the pull request instead of in prod.

npx airlock-rls

Or start from nextjs-supabase-starter — auth + a table with RLS + an isolation test, so a fresh table is safe by default.

FREE PDF

Grab the Supabase RLS cheat sheet

The golden rules, the footguns that leak in prod, correct policy snippets, and the isolation test — on one page.

FAQ

How is USING (true) different from RLS being off?

Both make the table publicly readable. RLS off means the engine doesn't check policies at all; USING (true) means RLS is on but the policy allows everyone. The audit is different — one shows up in pg_class.relrowsecurity, the other in pg_policies.qual — but the leak is the same.

Can I keep USING (true) for an internal table?

No — 'internal' tables are still reachable through the anon key. If a table is exposed to Supabase's API at all, USING (true) makes it public. Scope it to the caller, or don't expose it.