False positives we suppress on purpose
A scanner that cries wolf trains you to ignore it, which is worse than not having one. Four classes of result are suppressed by design so the first scan reads as believable and every remaining finding deserves attention.
Suppressions are documented rather than hidden, because an absent finding should mean checked-and-clean, not never-looked. Suppression is a promise about signal: what stays flagged should be actionable, and what is filtered should be enumerable. Four filters exist; this page enumerates them.
System schemas are excluded
Schemas owned by Postgres, Supabase or a managed extension, auth, storage, realtime, extensions, graphql, vault, pgsodium, cron, net, supabase_migrations among them, contain tables with RLS disabled by design. They are managed by the platform, not by you, and their contents are Supabase's responsibility rather than your migration's.
Flagging them produces a wall of unactionable criticals on the very first scan, which is the fastest way to make a security product feel broken. Detection is therefore scoped to developer-authored schemas.
The exclusion list lives in code as SYSTEM_SCHEMAS and is applied twice: coarsely inside the introspection SQL to keep result sets small, then authoritatively per row in the rule engine, so a newly managed schema cannot slip a finding through on a naming technicality.
One exception is deliberate: within storage, the storage.objects table remains in scope, because its RLS state decides whether private buckets stay private. A public flag on a bucket is only a promise if storage.objects enforces it.
RESTRICTIVE USING (true) is ignored
Postgres treats permissive and restrictive policies differently: permissive policies OR together, restrictive ones AND into the result. An always-true clause in an OR-chain grants everything; in an AND-chain it contributes nothing.
A RESTRICTIVE policy can only ever narrow access granted elsewhere, so USING (true) on one is a stylistic oddity rather than a hole, and the tautology rule walks past it. The permissive variant of the same expression is the opposite animal entirely and is flagged as critical, because a permissive always-true policy grants the whole table.
service_role-scoped policies are ignored
A policy targeting only service_role is decorative: that role carries BYPASSRLS and evaluates no policies at all. Reporting it as a critical is the single noisiest false positive available, made sharper because the Supabase dashboard generates exactly that policy by default.
The same suppression covers the other administrative roles that outrank RLS, postgres and supabase_admin among them. Policies targeting exclusively such roles describe privileges those roles already held regardless of expression, so there is nothing to fix and nothing gained by saying otherwise.
A 200 with an empty array is not a finding
From outside the database, PostgREST answering 200 with [] is ambiguous: RLS may have filtered every row, or the table may hold none. Calling that a critical puts a fabricated alarm in front of a first-time visitor roughly whenever an empty table exists, and empty tables are everywhere.
The probe records such tables as reachable rather than leaking, visible per table in the report, and raises ANON_TABLE_READABLE only when rows actually return. Silence with company is still silence, but it is reported silence rather than invented sound.
What suppression is not
None of this is leniency towards your own mistakes. A public flag on your bucket, an always-true policy you wrote, a bare auth.uid() on your column: all fire normally. The filters apply to platform-owned objects and to expressions that provably grant nothing, never to judgement calls about your intentions, which remain yours to make and ours to respect.
If a suppression looks wrong to you
Sometimes a suppressed class is exactly where your problem lives, for instance a policy you meant to bind anon but which was created scoped to service_role by accident. The findings explain what was evaluated, the rule documentation states scope, and Disputing a finding describes where to send the argument.
Corrections have somewhere concrete to land: the suppression list is data, not folklore, so a justified dispute adjusts SYSTEM_SCHEMAS or the rule predicates themselves, ships to every deployment, and writes its reasoning into the rule documentation.
Related questions
- Can I toggle suppressions per project?
- Not today; the list is deliberate and small, and a configuration surface for it does not exist. If your situation needs an exception, the dispute route is the working mechanism.
- Do suppressions hide leaks in platform tables?
- Platform schemas are Supabase-managed, so findings there would be noise aimed at you. Data you place in your own schemas is fully in scope on every scan.
Did this answer your question? If not, tell us what is missing — article corrections go straight to the person who maintains it.