-
Notifications
You must be signed in to change notification settings - Fork 0
[Security] Contacts POST route uses serviceClient, bypasses Supabase RLS #40
Copy link
Copy link
Open
Description
Summary
src/app/api/contacts/route.ts POST handler uses getServiceClient() (Supabase service role key) to upsert contacts. This bypasses Row Level Security policies.
Location
// src/app/api/contacts/route.ts line ~57
const serviceClient = getServiceClient();
const { data, error } = await serviceClient
.from("contacts")
.upsert(
{ user_id: user.id, phone, name: name || null },
{ onConflict: "user_id,phone" }
)Impact
While the user_id is taken from the authenticated session (not user input), the use of serviceClient means:
- Any RLS policies on the
contactstable are completely bypassed - If the
user.idwere ever manipulated upstream, there would be no database-level protection - This is inconsistent with GET handler which correctly uses the regular Supabase client with RLS
This is the same pattern identified in #24 (phone_numbers) and #30 (providers).
Suggested Fix
Use the regular Supabase client instead of getServiceClient() for the upsert:
const supabase = await createServerSupabaseClient();
const { data, error } = await supabase
.from("contacts")
.upsert(
{ user_id: user.id, phone, name: name || null },
{ onConflict: "user_id,phone" }
)Severity
Medium — RLS bypass, but user_id comes from authenticated session.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels