From e7222268e71811a88d663b179c2a1b72cc90aa14 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 17 Mar 2026 11:12:03 +0100 Subject: [PATCH 1/2] fix(security): enforce encrypted bundle updates --- ...ix_encrypted_bundle_update_enforcement.sql | 7 +++ tests/enforce-encrypted-bundles.test.ts | 49 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 supabase/migrations/20260317100429_fix_encrypted_bundle_update_enforcement.sql diff --git a/supabase/migrations/20260317100429_fix_encrypted_bundle_update_enforcement.sql b/supabase/migrations/20260317100429_fix_encrypted_bundle_update_enforcement.sql new file mode 100644 index 0000000000..09b9c6a922 --- /dev/null +++ b/supabase/migrations/20260317100429_fix_encrypted_bundle_update_enforcement.sql @@ -0,0 +1,7 @@ +-- Prevent direct PostgREST downgrades of encrypted bundles after insert. +DROP TRIGGER IF EXISTS enforce_encrypted_bundle_trigger ON public.app_versions; + +CREATE TRIGGER enforce_encrypted_bundle_trigger + BEFORE INSERT OR UPDATE OF session_key, key_id, app_id, owner_org ON public.app_versions + FOR EACH ROW + EXECUTE FUNCTION public.check_encrypted_bundle_on_insert(); diff --git a/tests/enforce-encrypted-bundles.test.ts b/tests/enforce-encrypted-bundles.test.ts index c9d4aff55e..a35c352b9a 100644 --- a/tests/enforce-encrypted-bundles.test.ts +++ b/tests/enforce-encrypted-bundles.test.ts @@ -259,6 +259,55 @@ describe('[Encrypted Bundles Enforcement]', () => { expect(error).toBeNull() expect(data?.name).toBe('1.0.0-direct-no-enforcement') }) + + it('should reject direct update that clears session_key when enforcement is enabled', async () => { + await getSupabaseClient() + .from('orgs') + .update({ enforce_encrypted_bundles: true }) + .eq('id', ORG_ID_ENCRYPTED) + + const bundleName = `1.0.0-direct-update-rejected-${Date.now()}` + const { data: inserted, error: insertError } = await getSupabaseClient() + .from('app_versions') + .insert({ + app_id: APP_NAME_ENCRYPTED, + name: bundleName, + checksum: 'd4e5f6789abcdef123456789abcdef123456789abcdef123456789abcdef12', + owner_org: ORG_ID_ENCRYPTED, + storage_provider: 'r2', + session_key: 'encrypted-session-key-for-direct-update', + }) + .select('id, session_key') + .single() + + expect(insertError).toBeNull() + expect(inserted?.session_key).toBe('encrypted-session-key-for-direct-update') + + const { error: updateError } = await getSupabaseClient() + .from('app_versions') + .update({ + session_key: '', + key_id: null, + }) + .eq('id', inserted!.id) + + expect(updateError).not.toBeNull() + expect(updateError?.message).toContain('encryption_required') + + const { data: afterUpdate, error: fetchError } = await getSupabaseClient() + .from('app_versions') + .select('session_key') + .eq('id', inserted!.id) + .single() + + expect(fetchError).toBeNull() + expect(afterUpdate?.session_key).toBe('encrypted-session-key-for-direct-update') + + await getSupabaseClient() + .from('orgs') + .update({ enforce_encrypted_bundles: false }) + .eq('id', ORG_ID_ENCRYPTED) + }) }) describe('helper Functions', () => { From 93e1dbe0566011b59efa4f819fbc71d9c615d2f0 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 17 Mar 2026 12:09:08 +0100 Subject: [PATCH 2/2] fix(db): narrow encrypted bundle update trigger --- ...20260317100429_fix_encrypted_bundle_update_enforcement.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/supabase/migrations/20260317100429_fix_encrypted_bundle_update_enforcement.sql b/supabase/migrations/20260317100429_fix_encrypted_bundle_update_enforcement.sql index 09b9c6a922..32065cd5cf 100644 --- a/supabase/migrations/20260317100429_fix_encrypted_bundle_update_enforcement.sql +++ b/supabase/migrations/20260317100429_fix_encrypted_bundle_update_enforcement.sql @@ -2,6 +2,8 @@ DROP TRIGGER IF EXISTS enforce_encrypted_bundle_trigger ON public.app_versions; CREATE TRIGGER enforce_encrypted_bundle_trigger - BEFORE INSERT OR UPDATE OF session_key, key_id, app_id, owner_org ON public.app_versions + -- app_id changes are already blocked and owner_org is auto-derived from app_id. + -- Limit UPDATE enforcement to encryption fields so regular metadata updates keep working. + BEFORE INSERT OR UPDATE OF session_key, key_id ON public.app_versions FOR EACH ROW EXECUTE FUNCTION public.check_encrypted_bundle_on_insert();