From ffd1cbffc30f114242cc4253c14b4bf872e347af Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Tue, 24 Mar 2026 14:19:58 -0700 Subject: [PATCH] fix: drop all anon RLS policies + revoke view access + add cache table Migration 002 locks down the Supabase telemetry backend: - Drops all SELECT, INSERT, UPDATE policies for the anon role - Explicitly revokes SELECT on crash_clusters and skill_sequences views - Drops stale error_message/failed_step columns (exist live but not in migration) - Creates community_pulse_cache table for server-side aggregation caching --- supabase/migrations/002_tighten_rls.sql | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 supabase/migrations/002_tighten_rls.sql diff --git a/supabase/migrations/002_tighten_rls.sql b/supabase/migrations/002_tighten_rls.sql new file mode 100644 index 00000000..6cf03b16 --- /dev/null +++ b/supabase/migrations/002_tighten_rls.sql @@ -0,0 +1,33 @@ +-- 002_tighten_rls.sql +-- Lock down all anon access. All reads/writes now go through edge functions +-- (which use SUPABASE_SERVICE_ROLE_KEY and bypass RLS). + +-- Drop all SELECT policies (anon key should not read telemetry data) +DROP POLICY IF EXISTS "anon_select" ON telemetry_events; +DROP POLICY IF EXISTS "anon_select" ON installations; +DROP POLICY IF EXISTS "anon_select" ON update_checks; + +-- Drop dangerous UPDATE policy (was unrestricted on all columns) +DROP POLICY IF EXISTS "anon_update_last_seen" ON installations; + +-- Drop INSERT policies (writes go through edge functions now) +DROP POLICY IF EXISTS "anon_insert_only" ON telemetry_events; +DROP POLICY IF EXISTS "anon_insert_only" ON installations; +DROP POLICY IF EXISTS "anon_insert_only" ON update_checks; + +-- Explicitly revoke view access (belt-and-suspenders) +REVOKE SELECT ON crash_clusters FROM anon; +REVOKE SELECT ON skill_sequences FROM anon; + +-- Drop stale columns that exist live but not in 001_telemetry.sql +ALTER TABLE telemetry_events DROP COLUMN IF EXISTS error_message; +ALTER TABLE telemetry_events DROP COLUMN IF EXISTS failed_step; + +-- Cache table for community-pulse aggregation (prevents DoS via repeated queries) +CREATE TABLE IF NOT EXISTS community_pulse_cache ( + id INTEGER PRIMARY KEY DEFAULT 1, + data JSONB NOT NULL DEFAULT '{}'::jsonb, + refreshed_at TIMESTAMPTZ DEFAULT now() +); +ALTER TABLE community_pulse_cache ENABLE ROW LEVEL SECURITY; +-- No anon policies — only service_role_key (used by edge functions) can read/write