RowShield

Help / Findings

How severities are assigned, and why they differ from instinct

All plansLast reviewed 2026-08-23

Severity encodes what an attacker gets, not how alarming a screen looks. That ordering occasionally disagrees with gut feel, and this page says why out loud, because a scale you cannot argue with is a scale you will ignore.

Critical

Four rules sit at critical: RLS_DISABLED and RLS_TAUTOLOGY from the catalog, ANON_TABLE_READABLE and SERVICE_ROLE_KEY_EXPOSED from the probe. Each means some or all data is readable now, or that a credential capable of reading everything is public. Critical is reserved for disclosure or total compromise; nothing else qualifies, which keeps the label meaningful.

PUBLIC_BUCKET_EXPOSURE normally rates high but escalates to critical in two cases: when a policy also permits anonymous writes, letting strangers host arbitrary content in your bucket, and when RLS is switched off on storage.objects itself.

High

RLS_NO_POLICIES and MISSING_WITH_CHECK rate high. No-policies denies every row to every client role: nothing is leaking, but the table is unreachable and something is quietly failing in production, which is an availability wound rather than a confidentiality one. Missing WITH CHECK leaves writes unconstrained while reads behave, so a caller can insert rows attributed to anyone.

PUBLIC_BUCKET_EXPOSURE defaults here: objects are served without auth checks, but only within a bucket you chose to publish. High covers integrity and availability failures: writes you did not authorise, access that silently broke, publishing wider than intended.

Medium

UNINDEXED_RLS_PREDICATE and RLS_UNWRAPPED_AUTH_CALL are performance rules. Neither exposes a row; both tax every query that touches the policy, which on a busy project converts directly into latency and invoice. They rate medium because the harm is cost, not disclosure, and mediums accumulate, which is why the health score caps them: twelve mediums cost 12 points, less than one critical.

Where instinct disagrees

Instinct often ranks a loudly failing feature above a silently open table, so RLS_NO_POLICIES feels worse than it is and tautologies feel better than they are. A tautology is reported as protected by every dashboard check while granting every row, which is why it outranks the deny-all case despite looking healthier on screen.

Severity feeds the health score directly, with saturating caps, so a stack of mediums never outweighs one critical there either. Where another tool orders things differently, including the advisor in the Supabase dashboard, which is a good instrument, adjudicate on the same question this scale uses: what would an attacker walk away with.

Related questions

Can a severity change per finding?
Yes, conditionally: PUBLIC_BUCKET_EXPOSURE escalates to critical when anonymous writes are possible, and rules that escalate or de-escalate do so per finding rather than per catalogue entry.

Did this answer your question? If not, tell us what is missing — article corrections go straight to the person who maintains it.

RowShield checks 9 rule classes continuously. This article describes shipped behaviour only.