Summary
The profiles table stores each user's email address, and its current SELECT policy lets any authenticated user read the full profile of every other user — including the email column.
Evidence
supabase/migrations/20260730000002_restrict_profiles_rls.sql:
CREATE POLICY "authenticated_users_can_view_profiles" ON public.profiles
FOR SELECT TO authenticated
USING (true);
This policy is OR'd with profiles_select_own from 20260729000000_add_profiles_rls_policy.sql. The legacy compat table public.users (created in 20260518000003_app_bootstrap_and_notifications.sql, which also stores email) has the same problem:
CREATE POLICY "Authenticated users can view users" ON public.users
FOR SELECT TO authenticated USING (true);
The app itself is affected: src/hooks/useMessages.ts fetches .from("profiles").select("*").neq("id", currentUserId) — pulling the email of up to 100 other users on every chat page load.
Exploit
Any logged-in account can dump the entire user directory:
-- via PostgREST /rest/v1/profiles?select=email,name
select id, name, email from public.profiles; -- RLS allows authenticated read of all rows
select email from public.users; -- legacy table, same issue
Email addresses are PII and enable targeted phishing, password-reset attacks, and account-takeover attempts against confirmed registered users.
Impact
- PII leak of every registered user's email address to any authenticated user.
- Enables email enumeration and targeted phishing / social engineering.
Suggested Fix
- Revoke column-level SELECT on
email from anon and authenticated roles so only the row owner (and service-role) can read it.
- Drop the permissive SELECT policy on the legacy
public.users table (or revoke SELECT entirely — the app no longer queries it).
- Update client code that currently selects
* from profiles (useMessages.ts, Profile.tsx, EditProfile.tsx) to select only the non-sensitive columns it actually renders.
Summary
The
profilestable stores each user'semailaddress, and its current SELECT policy lets any authenticated user read the full profile of every other user — including the email column.Evidence
supabase/migrations/20260730000002_restrict_profiles_rls.sql:This policy is OR'd with
profiles_select_ownfrom20260729000000_add_profiles_rls_policy.sql. The legacy compat tablepublic.users(created in20260518000003_app_bootstrap_and_notifications.sql, which also storesemail) has the same problem:The app itself is affected:
src/hooks/useMessages.tsfetches.from("profiles").select("*").neq("id", currentUserId)— pulling the email of up to 100 other users on every chat page load.Exploit
Any logged-in account can dump the entire user directory:
Email addresses are PII and enable targeted phishing, password-reset attacks, and account-takeover attempts against confirmed registered users.
Impact
Suggested Fix
emailfromanonandauthenticatedroles so only the row owner (and service-role) can read it.public.userstable (or revoke SELECT entirely — the app no longer queries it).*fromprofiles(useMessages.ts,Profile.tsx,EditProfile.tsx) to select only the non-sensitive columns it actually renders.