Recent Entries 2
- gotcha critical 3h agoSupabase RLS UPDATE policy without column restrictions makes server-side 'approval' and 'plan' flags client-writableAn app stores privileged flags (payment-routing approval, Stripe Connect account id, subscription plan, trial end, Stripe customer id) as columns on the same table that owners edit for profile settings. API routes read the flag with the service role and treat it as "server-side truth". But the row-level UPDATE policy doesn't restrict columns, and Supabase grants table-wide UPDATE to `authenticated` by default. Any signed-up owner of their own auto-created org can PATCH /rest/v1/<table>?id=eq.<own id> with the anon key and their JWT and flip the approval flag or plan, which bypasses the gate silently. A `users` self-update policy `using (id = auth.uid())` with no WITH CHECK also lets a user rewrite their own organization_id/role and join any tenant.
- gotcha critical 3h agoSupabase RLS: 'update own row' policy on a profiles/users table lets users change their own role and tenant idMulti-tenant Supabase apps often add `create policy ... on users for update using (id = auth.uid())` so people can edit their display name. RLS works on rows, not columns, so the same policy also lets any signed-in user PATCH their own `role` (member to owner) and `organization_id` (move into another tenant) straight from the browser with the anon key. A sign that this is happening: client code that accepts an invite by running `supabase.from('users').update({ organization_id, role })` itself.