From f727d5fb718428b23d455618f04bf22035878f7a Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Fri, 27 Feb 2026 02:26:30 +0000 Subject: [PATCH 1/6] fix(db): restrict upsert_version_meta rpc writes --- ...60227000000_harden_upsert_version_meta.sql | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 supabase/migrations/20260227000000_harden_upsert_version_meta.sql diff --git a/supabase/migrations/20260227000000_harden_upsert_version_meta.sql b/supabase/migrations/20260227000000_harden_upsert_version_meta.sql new file mode 100644 index 0000000000..c3bbf83fbc --- /dev/null +++ b/supabase/migrations/20260227000000_harden_upsert_version_meta.sql @@ -0,0 +1,100 @@ +-- ========================================================================== +-- Harden upsert_version_meta RPC against anonymous writes +-- ========================================================================== +CREATE OR REPLACE FUNCTION "public"."upsert_version_meta"( + "p_app_id" character varying, + "p_version_id" bigint, + "p_size" bigint +) RETURNS boolean + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' +AS $$ +DECLARE + existing_count integer; + v_app_owner_org uuid; + v_version_app_id character varying; + v_is_service_role boolean; +BEGIN + v_is_service_role := ( + ((SELECT auth.jwt() ->> 'role') = 'service_role') + OR ((SELECT session_user) IS NOT DISTINCT FROM 'postgres') + ); + + IF NOT v_is_service_role THEN + IF public.get_identity('{write,all}'::public.key_mode[]) IS NULL THEN + RETURN FALSE; + END IF; + + SELECT apps.owner_org INTO v_app_owner_org + FROM public.apps + WHERE apps.app_id = p_app_id + LIMIT 1; + + IF v_app_owner_org IS NULL THEN + RETURN FALSE; + END IF; + + IF NOT public.check_min_rights( + 'write'::public.user_min_right, + public.get_identity_org_appid('{write,all}'::public.key_mode[], v_app_owner_org, p_app_id), + v_app_owner_org, + p_app_id, + NULL::bigint + ) THEN + RETURN FALSE; + END IF; + + SELECT app_id INTO v_version_app_id + FROM public.app_versions + WHERE id = p_version_id + LIMIT 1; + + IF v_version_app_id IS DISTINCT FROM p_app_id THEN + RETURN FALSE; + END IF; + END IF; + + IF p_size > 0 THEN + -- Check for existing positive size + SELECT COUNT(*) INTO existing_count + FROM public.version_meta + WHERE public.version_meta.app_id = p_app_id + AND public.version_meta.version_id = p_version_id + AND public.version_meta.size > 0; + ELSIF p_size < 0 THEN + -- Check for existing negative size + SELECT COUNT(*) INTO existing_count + FROM public.version_meta + WHERE public.version_meta.app_id = p_app_id + AND public.version_meta.version_id = p_version_id + AND public.version_meta.size < 0; + ELSE + RETURN FALSE; + END IF; + + IF existing_count > 0 THEN + RETURN FALSE; + END IF; + + INSERT INTO public.version_meta (app_id, version_id, size) + VALUES (p_app_id, p_version_id, p_size); + + RETURN TRUE; + +EXCEPTION + WHEN unique_violation THEN + RETURN FALSE; +END; +$$; + +ALTER FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) OWNER TO "postgres"; + +REVOKE ALL ON FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) +FROM + "anon", + "authenticated"; + +GRANT +EXECUTE ON FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) +TO + "service_role"; From e687ff1160852809afc5a16bdbc0a2a7740dfaf9 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 3 Mar 2026 13:09:32 +0100 Subject: [PATCH 2/6] fix(db): simplify upsert_version_meta service-role guard --- ...60227000000_harden_upsert_version_meta.sql | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/supabase/migrations/20260227000000_harden_upsert_version_meta.sql b/supabase/migrations/20260227000000_harden_upsert_version_meta.sql index c3bbf83fbc..b4e73d2659 100644 --- a/supabase/migrations/20260227000000_harden_upsert_version_meta.sql +++ b/supabase/migrations/20260227000000_harden_upsert_version_meta.sql @@ -11,8 +11,6 @@ CREATE OR REPLACE FUNCTION "public"."upsert_version_meta"( AS $$ DECLARE existing_count integer; - v_app_owner_org uuid; - v_version_app_id character varying; v_is_service_role boolean; BEGIN v_is_service_role := ( @@ -21,37 +19,7 @@ BEGIN ); IF NOT v_is_service_role THEN - IF public.get_identity('{write,all}'::public.key_mode[]) IS NULL THEN - RETURN FALSE; - END IF; - - SELECT apps.owner_org INTO v_app_owner_org - FROM public.apps - WHERE apps.app_id = p_app_id - LIMIT 1; - - IF v_app_owner_org IS NULL THEN - RETURN FALSE; - END IF; - - IF NOT public.check_min_rights( - 'write'::public.user_min_right, - public.get_identity_org_appid('{write,all}'::public.key_mode[], v_app_owner_org, p_app_id), - v_app_owner_org, - p_app_id, - NULL::bigint - ) THEN - RETURN FALSE; - END IF; - - SELECT app_id INTO v_version_app_id - FROM public.app_versions - WHERE id = p_version_id - LIMIT 1; - - IF v_version_app_id IS DISTINCT FROM p_app_id THEN - RETURN FALSE; - END IF; + RETURN FALSE; END IF; IF p_size > 0 THEN From 4c355dbe8139d883685b7e6e999f3d8087129be6 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 3 Mar 2026 13:10:10 +0100 Subject: [PATCH 3/6] fix(db): remove runtime checks from upsert_version_meta --- .../20260227000000_harden_upsert_version_meta.sql | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/supabase/migrations/20260227000000_harden_upsert_version_meta.sql b/supabase/migrations/20260227000000_harden_upsert_version_meta.sql index b4e73d2659..9cd7ff863c 100644 --- a/supabase/migrations/20260227000000_harden_upsert_version_meta.sql +++ b/supabase/migrations/20260227000000_harden_upsert_version_meta.sql @@ -11,17 +11,7 @@ CREATE OR REPLACE FUNCTION "public"."upsert_version_meta"( AS $$ DECLARE existing_count integer; - v_is_service_role boolean; BEGIN - v_is_service_role := ( - ((SELECT auth.jwt() ->> 'role') = 'service_role') - OR ((SELECT session_user) IS NOT DISTINCT FROM 'postgres') - ); - - IF NOT v_is_service_role THEN - RETURN FALSE; - END IF; - IF p_size > 0 THEN -- Check for existing positive size SELECT COUNT(*) INTO existing_count From 08b50b726b11162ffb70ac2bbe400af32d8132e9 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 3 Mar 2026 13:11:30 +0100 Subject: [PATCH 4/6] fix(db): restrict upsert_version_meta execute privileges --- ...60228000000_restrict_upsert_version_meta_exec.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 supabase/migrations/20260228000000_restrict_upsert_version_meta_exec.sql diff --git a/supabase/migrations/20260228000000_restrict_upsert_version_meta_exec.sql b/supabase/migrations/20260228000000_restrict_upsert_version_meta_exec.sql new file mode 100644 index 0000000000..3641b9f830 --- /dev/null +++ b/supabase/migrations/20260228000000_restrict_upsert_version_meta_exec.sql @@ -0,0 +1,12 @@ +-- ========================================================================== +-- Restrict upsert_version_meta execute rights to service role only +-- ========================================================================== +REVOKE ALL ON FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) +FROM + "anon", + "authenticated"; + +GRANT +EXECUTE ON FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) +TO + "service_role"; From d819b589ac1065f724302141228c960c984acb24 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 3 Mar 2026 13:13:40 +0100 Subject: [PATCH 5/6] fix(supabase): keep upsert_version_meta privilege-only --- ...60227000000_harden_upsert_version_meta.sql | 49 ------------------- ...0000_restrict_upsert_version_meta_exec.sql | 12 ----- 2 files changed, 61 deletions(-) delete mode 100644 supabase/migrations/20260228000000_restrict_upsert_version_meta_exec.sql diff --git a/supabase/migrations/20260227000000_harden_upsert_version_meta.sql b/supabase/migrations/20260227000000_harden_upsert_version_meta.sql index 9cd7ff863c..203d1bfeba 100644 --- a/supabase/migrations/20260227000000_harden_upsert_version_meta.sql +++ b/supabase/migrations/20260227000000_harden_upsert_version_meta.sql @@ -1,52 +1,3 @@ --- ========================================================================== --- Harden upsert_version_meta RPC against anonymous writes --- ========================================================================== -CREATE OR REPLACE FUNCTION "public"."upsert_version_meta"( - "p_app_id" character varying, - "p_version_id" bigint, - "p_size" bigint -) RETURNS boolean - LANGUAGE "plpgsql" SECURITY DEFINER - SET "search_path" TO '' -AS $$ -DECLARE - existing_count integer; -BEGIN - IF p_size > 0 THEN - -- Check for existing positive size - SELECT COUNT(*) INTO existing_count - FROM public.version_meta - WHERE public.version_meta.app_id = p_app_id - AND public.version_meta.version_id = p_version_id - AND public.version_meta.size > 0; - ELSIF p_size < 0 THEN - -- Check for existing negative size - SELECT COUNT(*) INTO existing_count - FROM public.version_meta - WHERE public.version_meta.app_id = p_app_id - AND public.version_meta.version_id = p_version_id - AND public.version_meta.size < 0; - ELSE - RETURN FALSE; - END IF; - - IF existing_count > 0 THEN - RETURN FALSE; - END IF; - - INSERT INTO public.version_meta (app_id, version_id, size) - VALUES (p_app_id, p_version_id, p_size); - - RETURN TRUE; - -EXCEPTION - WHEN unique_violation THEN - RETURN FALSE; -END; -$$; - -ALTER FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) OWNER TO "postgres"; - REVOKE ALL ON FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) FROM "anon", diff --git a/supabase/migrations/20260228000000_restrict_upsert_version_meta_exec.sql b/supabase/migrations/20260228000000_restrict_upsert_version_meta_exec.sql deleted file mode 100644 index 3641b9f830..0000000000 --- a/supabase/migrations/20260228000000_restrict_upsert_version_meta_exec.sql +++ /dev/null @@ -1,12 +0,0 @@ --- ========================================================================== --- Restrict upsert_version_meta execute rights to service role only --- ========================================================================== -REVOKE ALL ON FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) -FROM - "anon", - "authenticated"; - -GRANT -EXECUTE ON FUNCTION "public"."upsert_version_meta"("p_app_id" character varying, "p_version_id" bigint, "p_size" bigint) -TO - "service_role"; From feae2c00159a4231316dc83baa5e21ebe791f7e7 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 3 Mar 2026 13:59:13 +0100 Subject: [PATCH 6/6] fix(db): rename upsert_version_meta migration to unique version --- ...a.sql => 20260227010000_restrict_upsert_version_meta_exec.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename supabase/migrations/{20260227000000_harden_upsert_version_meta.sql => 20260227010000_restrict_upsert_version_meta_exec.sql} (100%) diff --git a/supabase/migrations/20260227000000_harden_upsert_version_meta.sql b/supabase/migrations/20260227010000_restrict_upsert_version_meta_exec.sql similarity index 100% rename from supabase/migrations/20260227000000_harden_upsert_version_meta.sql rename to supabase/migrations/20260227010000_restrict_upsert_version_meta_exec.sql