-- AwoooP RLS Canary Wave 1: empty/low-risk tables only -- Date: 2026-05-12 -- -- Scope: -- These tables had exact production row_count=0 in the latest preflight: -- - awooop_contract_revisions -- - awooop_conversation_event -- - awooop_mcp_credential_refs -- - awooop_mcp_gateway_audit -- - awooop_mcp_grants -- - budget_ledger -- -- Safety: -- - fail-closed policy only; no NULL/empty-string app.project_id bypass. -- - aborts if any target is missing project_id, has NULL project_id, or has rows. -- - run with a migration/operator role, not through the production app role. -- - do not run until scripts/ops/awooop-rls-access-audit.py and -- scripts/ops/awooop-rls-manual-script-audit.py are green. BEGIN; SET LOCAL lock_timeout = '5s'; SET LOCAL statement_timeout = '30s'; CREATE TEMP TABLE _awooop_rls_wave1_targets ( table_name text PRIMARY KEY ) ON COMMIT DROP; INSERT INTO _awooop_rls_wave1_targets (table_name) VALUES ('awooop_contract_revisions'), ('awooop_conversation_event'), ('awooop_mcp_credential_refs'), ('awooop_mcp_gateway_audit'), ('awooop_mcp_grants'), ('budget_ledger'); DO $$ DECLARE target_table text; target_oid regclass; total_rows bigint; null_project_rows bigint; BEGIN FOR target_table IN SELECT table_name FROM _awooop_rls_wave1_targets ORDER BY table_name LOOP SELECT to_regclass(format('public.%I', target_table)) INTO target_oid; IF target_oid IS NULL THEN RAISE EXCEPTION 'RLS canary target table does not exist: %', target_table; END IF; IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = target_table AND column_name = 'project_id' ) THEN RAISE EXCEPTION 'RLS canary target missing project_id: %', target_table; END IF; EXECUTE format( 'SELECT COUNT(*), COUNT(*) FILTER (WHERE project_id IS NULL) FROM %I', target_table ) INTO total_rows, null_project_rows; IF null_project_rows <> 0 THEN RAISE EXCEPTION 'RLS canary target has NULL project_id rows: %, nulls=%', target_table, null_project_rows; END IF; IF total_rows <> 0 THEN RAISE EXCEPTION 'RLS canary wave1 only accepts empty tables: %, rows=%', target_table, total_rows; END IF; END LOOP; END $$; ALTER TABLE awooop_contract_revisions ENABLE ROW LEVEL SECURITY; ALTER TABLE awooop_contract_revisions FORCE ROW LEVEL SECURITY; DROP POLICY IF EXISTS awooop_contract_revisions_tenant ON awooop_contract_revisions; CREATE POLICY awooop_contract_revisions_tenant ON awooop_contract_revisions FOR ALL TO awooop_app USING (project_id = current_setting('app.project_id', TRUE)) WITH CHECK (project_id = current_setting('app.project_id', TRUE)); ALTER TABLE awooop_conversation_event ENABLE ROW LEVEL SECURITY; ALTER TABLE awooop_conversation_event FORCE ROW LEVEL SECURITY; DROP POLICY IF EXISTS awooop_conversation_event_tenant ON awooop_conversation_event; CREATE POLICY awooop_conversation_event_tenant ON awooop_conversation_event FOR ALL TO awooop_app USING (project_id = current_setting('app.project_id', TRUE)) WITH CHECK (project_id = current_setting('app.project_id', TRUE)); ALTER TABLE awooop_mcp_credential_refs ENABLE ROW LEVEL SECURITY; ALTER TABLE awooop_mcp_credential_refs FORCE ROW LEVEL SECURITY; DROP POLICY IF EXISTS awooop_mcp_credential_refs_tenant ON awooop_mcp_credential_refs; CREATE POLICY awooop_mcp_credential_refs_tenant ON awooop_mcp_credential_refs FOR ALL TO awooop_app USING (project_id = current_setting('app.project_id', TRUE)) WITH CHECK (project_id = current_setting('app.project_id', TRUE)); ALTER TABLE awooop_mcp_gateway_audit ENABLE ROW LEVEL SECURITY; ALTER TABLE awooop_mcp_gateway_audit FORCE ROW LEVEL SECURITY; DROP POLICY IF EXISTS awooop_mcp_gateway_audit_tenant ON awooop_mcp_gateway_audit; CREATE POLICY awooop_mcp_gateway_audit_tenant ON awooop_mcp_gateway_audit FOR ALL TO awooop_app USING (project_id = current_setting('app.project_id', TRUE)) WITH CHECK (project_id = current_setting('app.project_id', TRUE)); ALTER TABLE awooop_mcp_grants ENABLE ROW LEVEL SECURITY; ALTER TABLE awooop_mcp_grants FORCE ROW LEVEL SECURITY; DROP POLICY IF EXISTS awooop_mcp_grants_tenant ON awooop_mcp_grants; CREATE POLICY awooop_mcp_grants_tenant ON awooop_mcp_grants FOR ALL TO awooop_app USING (project_id = current_setting('app.project_id', TRUE)) WITH CHECK (project_id = current_setting('app.project_id', TRUE)); ALTER TABLE budget_ledger ENABLE ROW LEVEL SECURITY; ALTER TABLE budget_ledger FORCE ROW LEVEL SECURITY; DROP POLICY IF EXISTS budget_ledger_tenant ON budget_ledger; CREATE POLICY budget_ledger_tenant ON budget_ledger FOR ALL TO awooop_app USING (project_id = current_setting('app.project_id', TRUE)) WITH CHECK (project_id = current_setting('app.project_id', TRUE)); COMMIT;