-
-
Notifications
You must be signed in to change notification settings - Fork 76
Restrict invite_user_to_org RPC to authenticated callers #1710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,135 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- Harden invite_user_to_org RPC against anonymous enumeration and disclosure. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CREATE OR REPLACE FUNCTION public.invite_user_to_org( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "email" character varying, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "org_id" uuid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "invite_type" public.user_min_right | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) RETURNS character varying | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LANGUAGE plpgsql SECURITY DEFINER | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SET search_path = '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AS $$ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DECLARE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| org record; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| invited_user record; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| current_record record; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| current_tmp_user record; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| calling_user_id uuid; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| v_is_super_admin boolean := false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| v_use_rbac boolean := false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BEGIN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- Get the calling user's ID. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SELECT public.get_identity_org_allowed('{read,upload,write,all}'::public.key_mode[], invite_user_to_org.org_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| INTO calling_user_id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- Treat missing orgs as unauthorized to avoid org existence enumeration. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SELECT * INTO org FROM public.orgs WHERE public.orgs.id=invite_user_to_org.org_id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF org IS NULL OR calling_user_id IS NULL THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'NO_RIGHTS'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- Check if user has at least public.rbac_right_admin() rights. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF NOT public.check_min_rights(public.rbac_right_admin()::public.user_min_right, calling_user_id, invite_user_to_org.org_id, NULL::varchar, NULL::bigint) THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PERFORM public.pg_log('deny: NO_RIGHTS_ADMIN', jsonb_build_object('org_id', invite_user_to_org.org_id, 'invite_type', invite_user_to_org.invite_type)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'NO_RIGHTS'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- If inviting as super_admin, caller must be super_admin. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF (invite_type = public.rbac_right_super_admin()::public.user_min_right OR invite_type = public.rbac_right_invite_super_admin()::public.user_min_right) THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| v_use_rbac := public.rbac_is_enabled_for_org(invite_user_to_org.org_id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF v_use_rbac THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SELECT EXISTS ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SELECT 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FROM public.role_bindings rb | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JOIN public.roles r ON r.id = rb.role_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WHERE rb.principal_type = public.rbac_principal_user() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AND rb.principal_id = calling_user_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AND ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (rb.scope_type = public.rbac_scope_org() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AND rb.org_id = invite_user_to_org.org_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AND r.name = public.rbac_role_org_super_admin()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OR | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (rb.scope_type = public.rbac_scope_platform() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AND r.name = public.rbac_role_platform_super_admin()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) INTO v_is_super_admin; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF NOT v_is_super_admin THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PERFORM public.pg_log('deny: NO_RIGHTS_SUPER_ADMIN', jsonb_build_object('org_id', invite_user_to_org.org_id, 'invite_type', invite_user_to_org.invite_type)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'NO_RIGHTS'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF org.enforcing_2fa AND NOT public.has_2fa_enabled(auth.uid()) THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PERFORM public.pg_log('deny: SUPER_ADMIN_2FA_REQUIRED', jsonb_build_object('org_id', invite_user_to_org.org_id, 'invite_type', invite_user_to_org.invite_type, 'uid', auth.uid())); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'NO_RIGHTS'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELSE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF NOT public.check_min_rights(public.rbac_right_super_admin()::public.user_min_right, calling_user_id, invite_user_to_org.org_id, NULL::varchar, NULL::bigint) THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PERFORM public.pg_log('deny: NO_RIGHTS_SUPER_ADMIN', jsonb_build_object('org_id', invite_user_to_org.org_id, 'invite_type', invite_user_to_org.invite_type)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'NO_RIGHTS'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- Check if user already exists. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SELECT public.users.id INTO invited_user FROM public.users WHERE public.users.email=invite_user_to_org.email; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF invited_user IS NOT NULL THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- User exists, check if already in org. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SELECT public.org_users.id INTO current_record | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FROM public.org_users | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WHERE public.org_users.user_id=invited_user.id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AND public.org_users.org_id=invite_user_to_org.org_id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF current_record IS NOT NULL THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'ALREADY_INVITED'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELSE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- Add user to org. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| INSERT INTO public.org_users (user_id, org_id, user_right) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| VALUES (invited_user.id, invite_user_to_org.org_id, invite_type); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'OK'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELSE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- User doesn't exist, check tmp_users for pending invitations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SELECT * INTO current_tmp_user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FROM public.tmp_users | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WHERE public.tmp_users.email=invite_user_to_org.email | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AND public.tmp_users.org_id=invite_user_to_org.org_id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF current_tmp_user IS NOT NULL THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- Invitation already exists. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF current_tmp_user.cancelled_at IS NOT NULL THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- Invitation was cancelled, check if recent. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IF current_tmp_user.cancelled_at > (CURRENT_TIMESTAMP - INTERVAL '3 hours') THEN | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'TOO_RECENT_INVITATION_CANCELATION'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELSE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'NO_EMAIL'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELSE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'ALREADY_INVITED'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELSE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- No invitation exists, need to create one (handled elsewhere). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RETURN 'NO_EMAIL'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| END; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $$; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| REVOKE EXECUTE ON FUNCTION public.invite_user_to_org( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| character varying, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uuid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public.user_min_right | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) FROM "anon"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+119
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GRANT EXECUTE ON FUNCTION public.invite_user_to_org( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| character varying, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uuid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public.user_min_right | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) TO "authenticated"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GRANT EXECUTE ON FUNCTION public.invite_user_to_org( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| character varying, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uuid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public.user_min_right | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) TO "service_role"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+119
to
+135
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Search for the migration file
fd -type f -name "*fix_invite_user_to_org_security.sql"Repository: Cap-go/capgo Length of output: 226 🏁 Script executed: # Also check for any existing invite_user_to_org function definitions
rg -n "CREATE.*FUNCTION.*invite_user_to_org|DROP FUNCTION.*invite_user_to_org" supabase/migrations/Repository: Cap-go/capgo Length of output: 1140 🏁 Script executed: # Search for patterns of PUBLIC revokes in other migrations
rg -n "REVOKE EXECUTE.*FROM PUBLIC|REVOKE.*FROM \"PUBLIC\"" supabase/migrations/ -A 2 -B 2Repository: Cap-go/capgo Length of output: 694 🏁 Script executed: # Read the target migration file to understand full context
cat -n supabase/migrations/20260227150000_fix_invite_user_to_org_security.sqlRepository: Cap-go/capgo Length of output: 6138 🏁 Script executed: # Check the previous migration that modified invite_user_to_org
cat -n supabase/migrations/20260203201308_rbac_org_member_no_app_access.sql | head -250Repository: Cap-go/capgo Length of output: 10909 🏁 Script executed: # Search for all ACL modifications to invite_user_to_org function
rg -n "GRANT|REVOKE.*invite_user_to_org" supabase/migrations/ -B 2 -A 2Repository: Cap-go/capgo Length of output: 50369 🏁 Script executed: # Check if there are any ALTER DEFAULT PRIVILEGES statements affecting functions
rg -n "ALTER DEFAULT PRIVILEGES" supabase/migrations/ -A 2 -B 2Repository: Cap-go/capgo Length of output: 3833 🌐 Web query:
💡 Result: In PostgreSQL, What can make it look like “PUBLIC got EXECUTE again”:
So: Yes, OR REPLACE preserves ACLs for Sources: PostgreSQL [1] (postgresql.org) Citations:
Add explicit The function inherits Proposed fix+REVOKE EXECUTE ON FUNCTION public.invite_user_to_org(
+ character varying,
+ uuid,
+ public.user_min_right
+) FROM PUBLIC;
+
REVOKE EXECUTE ON FUNCTION public.invite_user_to_org(
character varying,
uuid,
public.user_min_right
) FROM "anon";📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 128
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 128
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 126
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 1409
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 2029
🏁 Script executed:
fd "fix_invite_user_to_org_security" supabase/migrations/Repository: Cap-go/capgo
Length of output: 126
🏁 Script executed:
ls -lh supabase/migrations/ | tail -30Repository: Cap-go/capgo
Length of output: 3016
🏁 Script executed:
rg -l "fix_invite_user_to_org_security" supabase/migrations/Repository: Cap-go/capgo
Length of output: 38
🏁 Script executed:
git ls-files supabase/migrations/ | tail -10Repository: Cap-go/capgo
Length of output: 745
🏁 Script executed:
rg -l "SUPER_ADMIN_2FA_REQUIRED" supabase/Repository: Cap-go/capgo
Length of output: 221
🏁 Script executed:
rg -B5 -A5 "SUPER_ADMIN_2FA_REQUIRED" supabase/Repository: Cap-go/capgo
Length of output: 6352
🏁 Script executed:
cat -n supabase/migrations/20260227150000_fix_invite_user_to_org_security.sql | head -100Repository: Cap-go/capgo
Length of output: 5007
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 6138
🏁 Script executed:
rg -A10 "CREATE.*FUNCTION.*get_identity_org_allowed" supabase/Repository: Cap-go/capgo
Length of output: 3596
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 4242
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 2714
🏁 Script executed:
rg -A8 "CREATE OR REPLACE FUNCTION public.has_2fa_enabled" supabase/schemas/prod.sqlRepository: Cap-go/capgo
Length of output: 38
🏁 Script executed:
cat -n supabase/migrations/20260102140000_fix_get_identity_hashed_apikeys.sql | head -100Repository: Cap-go/capgo
Length of output: 4239
🏁 Script executed:
sed -n '100,160p' supabase/migrations/20260102140000_fix_get_identity_hashed_apikeys.sqlRepository: Cap-go/capgo
Length of output: 1957
🏁 Script executed:
Repository: Cap-go/capgo
Length of output: 1620
Use the resolved caller identity for 2FA checks and audit logging.
Authorization is checked against
calling_user_id(resolved viaget_identity_org_allowed()to handle both JWT and API key auth), but the 2FA check at lines 62-63 switches toauth.uid(). In service-role or API-key-mediated flows,auth.uid()is NULL whilecalling_user_idcontains the actual authenticated user, causing the 2FA check to be bypassed or invalid and audit logging to record NULL instead of the actual caller.🔧 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents