Summary
react-doctor/supabase-rls-policy-risk produces false positives on three common, safe Supabase RLS patterns because it lints each migration file in isolation and pattern-matches policy text without resolving the final schema state. We hit all three on a real Next.js + Supabase app (v0.5.7); after independent review each flagged line was confirmed safe. Filing because the rule's value is high and these would be cheap to fix.
The three false-positive classes
1. Cross-migration DROP / REPLACE not followed. A broad for all policy created in migration A is dropped and recreated as for insert-only in a later migration B. The rule flags the original for all in A even though that policy no longer exists in the live schema.
-- migration A (flagged)
create policy "p" on t for all using (is_admin()) with check (is_admin());
-- migration B (the rule never sees this when linting A)
drop policy if exists "p" on t;
create policy "p_insert" on t for insert with check (is_admin());
2. to service_role read as a "bypass" when it is a tightening. Scoping an INSERT policy to service_role removes anon/authenticated write access — it's the fix, not the risk. service_role is server-only and never shipped to the browser, so the only role the policy admits is unreachable by an attacker.
-- flagged as "references a service-role bypass" — but this is the hardening commit
create policy "system_insert" on audit_log for insert to service_role with check (true);
3. ALTER TABLE IF EXISTS ... DISABLE ROW LEVEL SECURITY on an already-dropped table. A cleanup migration that tears down policies for tables dropped earlier is flagged as "disables RLS," but IF EXISTS makes it a no-op at apply time (the table doesn't exist).
-- pure cleanup migration; table was dropped in an earlier migration
alter table if exists public.old_table disable row level security; -- flagged, but no-op
Suggested fixes (any one helps)
- Make the rule migration-folder-aware: resolve the final policy state across the migration set before flagging (so a dropped/replaced policy isn't reported).
- Treat a policy scoped
to service_role (or any non-public, non-anon, non-authenticated role) as not-publicly-writable, rather than as a bypass.
- Recognize
IF EXISTS guards / dropped-table cleanup so DISABLE ROW LEVEL SECURITY on a non-existent table isn't flagged.
Workaround
Suppressing per-file via ignore.overrides in doctor.config.ts. Happy to provide more detail if useful. Thanks for the tool.
Summary
react-doctor/supabase-rls-policy-riskproduces false positives on three common, safe Supabase RLS patterns because it lints each migration file in isolation and pattern-matches policy text without resolving the final schema state. We hit all three on a real Next.js + Supabase app (v0.5.7); after independent review each flagged line was confirmed safe. Filing because the rule's value is high and these would be cheap to fix.The three false-positive classes
1. Cross-migration
DROP/REPLACEnot followed. A broadfor allpolicy created in migration A is dropped and recreated asfor insert-only in a later migration B. The rule flags the originalfor allin A even though that policy no longer exists in the live schema.2.
to service_roleread as a "bypass" when it is a tightening. Scoping an INSERT policyto service_roleremoves anon/authenticated write access — it's the fix, not the risk.service_roleis server-only and never shipped to the browser, so the only role the policy admits is unreachable by an attacker.3.
ALTER TABLE IF EXISTS ... DISABLE ROW LEVEL SECURITYon an already-dropped table. A cleanup migration that tears down policies for tables dropped earlier is flagged as "disables RLS," butIF EXISTSmakes it a no-op at apply time (the table doesn't exist).Suggested fixes (any one helps)
to service_role(or any non-public, non-anon, non-authenticatedrole) as not-publicly-writable, rather than as a bypass.IF EXISTSguards / dropped-table cleanup soDISABLE ROW LEVEL SECURITYon a non-existent table isn't flagged.Workaround
Suppressing per-file via
ignore.overridesindoctor.config.ts. Happy to provide more detail if useful. Thanks for the tool.