-- ============================================================================ -- CRITICAL SECURITY FIXES (P0) -- ============================================================================ -- Fix #1: Profiles Table PII Exposure - Remove anon access DROP POLICY IF EXISTS "Public can view non-banned public profiles" ON public.profiles; CREATE POLICY "Profiles restricted to authenticated users and moderators" ON public.profiles FOR SELECT TO authenticated USING ( auth.uid() = user_id OR is_moderator(auth.uid()) ); GRANT SELECT ON public.filtered_profiles TO anon; COMMENT ON POLICY "Profiles restricted to authenticated users and moderators" ON public.profiles IS 'SECURITY: Restricts direct profile table access to profile owners and moderators only. Anonymous users must use filtered_profiles view.'; -- Fix #2: User_roles Table - Ensure no public access ALTER TABLE public.user_roles ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "user_roles are viewable by everyone" ON public.user_roles; DROP POLICY IF EXISTS "Public can view user roles" ON public.user_roles; DROP POLICY IF EXISTS "Users can view their own roles" ON public.user_roles; CREATE POLICY "Users can view their own roles only" ON public.user_roles FOR SELECT TO authenticated USING (auth.uid() = user_id); CREATE POLICY "Moderators can view all roles with MFA" ON public.user_roles FOR SELECT TO authenticated USING (is_moderator(auth.uid())); -- Fix #3: Error_summary View - Set SECURITY INVOKER DROP VIEW IF EXISTS error_summary; CREATE VIEW error_summary WITH (security_invoker = true) AS SELECT error_type, endpoint, COUNT(*) as occurrence_count, COUNT(DISTINCT user_id) as affected_users, MAX(created_at) as last_occurred, MIN(created_at) as first_occurred, ROUND(AVG(duration_ms)::numeric, 2) as avg_duration_ms, (ARRAY_AGG(request_id ORDER BY created_at DESC) FILTER (WHERE created_at > now() - interval '1 day') )[1:5] as recent_request_ids FROM request_metadata WHERE error_type IS NOT NULL AND created_at > now() - interval '30 days' GROUP BY error_type, endpoint ORDER BY occurrence_count DESC; DROP POLICY IF EXISTS "Moderators can view error metadata" ON request_metadata; CREATE POLICY "Moderators can view error metadata" ON request_metadata FOR SELECT TO authenticated USING ( is_moderator(auth.uid()) OR user_id = auth.uid() ); GRANT SELECT ON error_summary TO authenticated; COMMENT ON VIEW error_summary IS 'Error aggregation view with SECURITY INVOKER - uses caller permissions.';