78c19291c9
023_security_hardening.sql: - Item 1 (CRITICAL, advisor lint 0010): founder_pricing_seats view → recreate with security_invoker=on so it respects RLS instead of running as definer. (The founder counter no longer depends on it — item 0 uses Stripe directly.) - Item 3: waitlist write hole — drop the always-true policies, anon may INSERT only, update/delete/read via service role. - Item 5: pin an explicit search_path on the flagged functions (lint 0011). 024_anon_revoke_discoverability.sql: - Item 4: revoke anon SELECT on the advisor-named tables (accuracy_tracking, bets, cascade_alerts, closing_lines, coach_profiles, daily_scan) + a commented broad sweep. The frontend reads data via Express (service role), never as anon, so this is safe. REVOKE/KEEP rationale documented in the file. These need Kev to apply (no DB access from here); fingerprint = re-run the Security Advisor and confirm the lints clear. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
64 lines
3.2 KiB
PL/PgSQL
64 lines
3.2 KiB
PL/PgSQL
-- Migration 023 — Security hardening (Chrome ops session follow-up)
|
|
-- Author: security follow-up items 1, 3, 5. Apply in the Supabase SQL editor.
|
|
-- Idempotent + guarded so a re-run is safe. After applying, re-run the Security
|
|
-- Advisor to confirm lint 0010 (security_definer_view) and 0011
|
|
-- (function_search_path_mutable) are cleared.
|
|
|
|
begin;
|
|
|
|
-- ── Item 1 (CRITICAL, advisor lint 0010) ────────────────────────────────────
|
|
-- public.founder_pricing_seats is a SECURITY DEFINER view — it runs with the
|
|
-- creator's privileges and ignores RLS, exposed via the public API. Recreate
|
|
-- it as security_invoker so it runs with the CALLER's privileges + respects RLS.
|
|
-- (The founder counter no longer depends on this view — it now counts real
|
|
-- active Stripe subscriptions directly — so this is purely closing the surface.)
|
|
do $$
|
|
begin
|
|
if exists (select 1 from pg_views where schemaname = 'public' and viewname = 'founder_pricing_seats') then
|
|
execute 'alter view public.founder_pricing_seats set (security_invoker = on)';
|
|
end if;
|
|
end $$;
|
|
|
|
-- ── Item 3 — waitlist write hole ────────────────────────────────────────────
|
|
-- public.waitlist had always-true (USING(true)/WITH CHECK(true)) write policies:
|
|
-- the anon API could insert/update/delete rows. Drop ALL existing policies and
|
|
-- allow anon to INSERT only; updates/deletes/reads go through the service role
|
|
-- (which bypasses RLS). Signups are additionally rate-limited at the API layer.
|
|
alter table public.waitlist enable row level security;
|
|
do $$
|
|
declare pol record;
|
|
begin
|
|
for pol in select policyname from pg_policies where schemaname = 'public' and tablename = 'waitlist' loop
|
|
execute format('drop policy %I on public.waitlist', pol.policyname);
|
|
end loop;
|
|
end $$;
|
|
create policy waitlist_anon_insert on public.waitlist for insert to anon with check (true);
|
|
-- Remove any lingering table-level write grants from anon; keep INSERT only.
|
|
revoke update, delete, truncate on public.waitlist from anon;
|
|
revoke select on public.waitlist from anon; -- a signup list is not public
|
|
grant insert on public.waitlist to anon;
|
|
|
|
-- ── Item 5 — function search_path hardening (advisor lint 0011) ──────────────
|
|
-- Flagged functions have a mutable search_path (hijackable). Pin an explicit,
|
|
-- safe search_path (pg_catalog, public) — resolves the advisor without the
|
|
-- breakage risk of '' on functions that reference public objects unqualified.
|
|
-- Handles any overload signature.
|
|
do $$
|
|
declare fn record;
|
|
begin
|
|
for fn in
|
|
select p.oid::regprocedure as sig
|
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
where n.nspname = 'public'
|
|
and p.proname in ('touch_updated_at', 'update_updated_at', 'reset_scan_count')
|
|
loop
|
|
execute format('alter function %s set search_path = pg_catalog, public', fn.sig);
|
|
end loop;
|
|
end $$;
|
|
|
|
commit;
|
|
|
|
-- NOTE: item 5 lists "and the other flagged functions". Run the Security Advisor
|
|
-- (Supabase -> Advisors -> Security, lint 0011) for the full list; add each to
|
|
-- the proname IN (...) set above and re-apply. All are the same low-risk change.
|