RowShield
Guides

Tautological policies: when RLS is switched on and says nothing

A tautological RLS policy is one whose condition is always true — most commonly USING (true) — so it admits every row of the table to every role it targets. The policy machinery exists, the RLS flag reads enabled, the dashboard reports the table protected, and the filtering does nothing whatsoever.

This page defines the term precisely, explains why everything looks fine while the table is wide open, gives you a query to find your own, and shows the fix. Detection is automated: RLS_TAUTOLOGY backs every claim here on every scheduled scan.

Rules that check this

What one looks like

The canonical specimen grants reads to everyone with a condition that cannot fail:

-- Looks like protection, filters like nothing:
CREATE POLICY "anyone_can_read_projects"
  ON public.projects
  FOR SELECT
  TO authenticated
  USING (true);

Why people write them

Nobody writes USING (true) intending a leak, which is why the pattern survives reviews. It appears when a feature is blocked mid-debugging and the policy gets relaxed to unblock it, with the narrowing promised for later; when tutorial copy is pasted wholesale; when generators emit a permissive default because intent was unknowable; and when "this table really is public" gets expressed as a tautology instead of a deliberate decision. The first two are deferred work. The third is a generator guessing. Only the last is design, and it should say so loudly enough that a reviewer recognises it months later.

Why everything still looks fine

RLS enabled is exactly what the flag reports, so configuration views show green. Requests succeed — that is the problem — so no error surfaces in any log. The advisor eventually flags constant-true policies, but it is a page opened when something already drew attention. Meanwhile the failure sits in an awkward middle: unlike RLS disabled, the machinery is present; unlike no policies, it denies no one. Both neighbours get caught by different instincts, which is how the tautology becomes the state most likely to survive unnoticed between audits.

Finding yours

Text search over pg_policies catches the constant forms — true, 1 = 1, NOT false — and also matches expressions that merely contain the word, so read the results rather than counting them:

SELECT tablename, policyname, cmd, permissive, qual, with_check
FROM pg_policies
WHERE schemaname = 'public'
  AND (qual ~* '(^|\()\s*true\s*(\)|$)'
       OR with_check ~* '(^|\()\s*true\s*(\)|$)');

Two legitimate cases, and the fix

Two uses of a constant true are sound. Genuinely public reference data — postcodes, feature flags, category labels — may admit everyone by design. And RESTRICTIVE policies exist only to narrow, so a restrictive USING (true) grants nothing; scanners that ignore that distinction cry wolf. The dangerous form is a permissive tautology substituting for a decision, and its fix is replacing the constant with the predicate that encodes ownership:

ALTER POLICY "anyone_can_read_projects"
  ON public.projects
  FOR SELECT
  TO authenticated
  USING (user_id = (SELECT auth.uid()));

How it stays detected

RLS_TAUTOLOGY normalises the deparsed expression of every developer policy and flags permissive policies whose condition is a constant true, including the disguised forms. The two legitimate cases are respected: restrictive-true is ignored, and policies scoped solely to service_role are ignored because that role bypasses everything anyway. Findings arrive with the ALTER POLICY generated from your real columns, and the rule re-runs on every scheduled scan, so a narrowed-back policy that regresses returns as a regression rather than a discovery. RowShield reads pg_catalog metadata only and is an independent product, not affiliated with or endorsed by Supabase. Run a free audit at rowshield.dev/audit to see the public half today.

Frequently asked

Is USING (true) ever correct?
Yes, twice: on genuinely public data, and on RESTRICTIVE policies whose job is narrowing rather than granting. It becomes a vulnerability when a permissive constant substitutes for a decision about who owns which rows.
How is a tautology different from having no policies?
Opposite failures on the same green-looking flag: zero policies deny every row to every client role, while a tautology admits every row. Both hide behind RLS enabled, which is why the flag alone settles neither.
Does WITH CHECK (true) matter too?
Yes. On write commands a constant-true WITH CHECK validates every attempted insert or update, so reads can be locked down tightly while writes remain attributed-to-anyone — the pair needs auditing together.

Check your project in about ten seconds

Paste a URL. No signup, no writes, nothing stored.

Run the free audit
what is tautological policysupabase using true policyrls policy always truetautological rls policy