A bug we caught before it shipped
Every multi-tenant starter kit promises isolation. This is the exact policy that would have broken it in ours — found in a pre-release audit, not by a customer.
The insert policy on organization_members checked who was joining, but never which organization. Any authenticated user could grant themselves membership — including role: 'owner' — in any organization, just by knowing its id.
✓ FIXED
The policy is scoped to organizations the caller actually owns — the only legitimate client-side path to create a membership row. Invite acceptance happens separately, server-side, via a trigger that bypasses RLS entirely.
Read the full write-up → the exploit, the blast radius, and how to prove your own policies hold.
1-- ✕ checks who, not which org — anyone can join anyone's org 2create policy "Users can insert their own membership" 3on organization_members for insert 4with check (user_id = auth.uid()); 5 6-- ✓ fixed: scoped to orgs the caller actually owns 7create policy "Owners can add themselves when creating an org" 8on organization_members for insert 9with check ( 10 user_id = auth.uid() 11 and role = 'owner' 12 and organization_id in ( 13 select id from organizations where owner_id = auth.uid() 14 ) 15);