From 17e1efc421a7d882a6cf6bf6898eedd5940ec554 Mon Sep 17 00:00:00 2001 From: QSchlegel Date: Mon, 6 Jul 2026 10:26:29 +0200 Subject: [PATCH] fix(db): enable RLS + deny-all PostgREST policies on tables added since the original RLS migration The Supabase security advisor flags rls_disabled_in_public (ERROR) on the seven tables created after 20251215090000_enable_rls_disable_postgrest: PendingBot, BotClaimToken, AuditLog, Contact, WalletBotAccess, BotKey, BotUser. ProposalTally and the three notification-center tables would be flagged as soon as they reach production, so they are included too. Same idempotent pattern as the original migration: RLS on, deny-all for anon/authenticated when those roles exist, Prisma service role unaffected. Co-Authored-By: Claude Fable 5 --- .../migration.sql | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 prisma/migrations/20260706100000_enable_rls_followup_tables/migration.sql diff --git a/prisma/migrations/20260706100000_enable_rls_followup_tables/migration.sql b/prisma/migrations/20260706100000_enable_rls_followup_tables/migration.sql new file mode 100644 index 00000000..bd8973a7 --- /dev/null +++ b/prisma/migrations/20260706100000_enable_rls_followup_tables/migration.sql @@ -0,0 +1,43 @@ +-- Enable Row Level Security (RLS) and deny-all policies for PostgREST roles +-- on tables created after 20251215090000_enable_rls_disable_postgrest. +-- Covers the Supabase security advisor's rls_disabled_in_public findings +-- (PendingBot, BotClaimToken, AuditLog, Contact, WalletBotAccess, BotKey, +-- BotUser) plus tables that land in the same deploy and would be flagged +-- next (ProposalTally, notification center tables). +-- +-- Same contract as the original migration: +-- - Enables RLS on each table unconditionally (skipping tables that don't exist) +-- - Only creates deny-all policies for `anon` and `authenticated` roles if they exist +-- - Prisma (service role / table owner) continues to bypass RLS + +DO $$ +DECLARE + tbl TEXT; +BEGIN + FOR tbl IN + SELECT unnest(ARRAY[ + 'PendingBot', 'BotClaimToken', 'BotUser', 'BotKey', 'WalletBotAccess', + 'Contact', 'AuditLog', 'ProposalTally', + 'WalletSignerNotificationSetting', 'EmailVerificationToken', 'NotificationDelivery' + ]) + LOOP + -- Skip tables that don't exist + IF EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = tbl) THEN + EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', tbl); + + IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'anon') THEN + EXECUTE format( + 'CREATE POLICY "deny_all_anon_%s" ON %I FOR ALL TO anon USING (false) WITH CHECK (false)', + tbl, tbl + ); + END IF; + + IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated') THEN + EXECUTE format( + 'CREATE POLICY "deny_all_authenticated_%s" ON %I FOR ALL TO authenticated USING (false) WITH CHECK (false)', + tbl, tbl + ); + END IF; + END IF; + END LOOP; +END $$;