diff --git a/.changeset/clean-json-arrays.md b/.changeset/clean-json-arrays.md new file mode 100644 index 0000000..f6f988b --- /dev/null +++ b/.changeset/clean-json-arrays.md @@ -0,0 +1,5 @@ +--- +'@ankhorage/supabase-vault': patch +--- + +Parse serialized configured-field arrays through PostgreSQL text before converting them to JSONB so Bun SQL does not expose them as JSON scalar strings during secret creation or replacement. diff --git a/src/adapter.test.ts b/src/adapter.test.ts index 95b7a3b..ae72bc1 100644 --- a/src/adapter.test.ts +++ b/src/adapter.test.ts @@ -69,7 +69,8 @@ describe('createSupabaseVaultAdapter', () => { expect(JSON.stringify(result)).not.toContain('vault-id'); const [, , insertCall] = client.calls; - expect(insertCall?.sql).toContain('array(select jsonb_array_elements_text($7::jsonb))'); + expect(insertCall?.sql).toContain('array(select jsonb_array_elements_text($7::text::jsonb))'); + expect(insertCall?.sql).not.toContain('jsonb_array_elements_text($7::jsonb)'); expect(insertCall?.parameters[6]).toBe('["clientId","clientSecret"]'); expect(Array.isArray(insertCall?.parameters[6])).toBe(false); }); @@ -104,7 +105,8 @@ describe('createSupabaseVaultAdapter', () => { expect(JSON.stringify(result)).not.toContain('ROTATED_SECRET'); const [, , updateCall] = client.calls; - expect(updateCall?.sql).toContain('select jsonb_array_elements_text($4::jsonb)'); + expect(updateCall?.sql).toContain('select jsonb_array_elements_text($4::text::jsonb)'); + expect(updateCall?.sql).not.toContain('jsonb_array_elements_text($4::jsonb)'); expect(updateCall?.parameters[3]).toBe('["clientSecret"]'); expect(Array.isArray(updateCall?.parameters[3])).toBe(false); }); diff --git a/src/adapter.ts b/src/adapter.ts index b201f3e..11ecb8e 100644 --- a/src/adapter.ts +++ b/src/adapter.ts @@ -122,7 +122,7 @@ export function createSupabaseVaultAdapter( (project_id, environment, secret_ref, vault_secret_id, kind, provider, configured_fields) values ( $1, $2, $3, $4::uuid, $5, $6, - array(select jsonb_array_elements_text($7::jsonb)) + array(select jsonb_array_elements_text($7::text::jsonb)) ) returning project_id, environment, secret_ref, kind, provider, configured_fields, created_at::text, updated_at::text`, @@ -168,7 +168,7 @@ export function createSupabaseVaultAdapter( const updated = await executor.query( `update ${metadataTable} set configured_fields = array( - select jsonb_array_elements_text($4::jsonb) + select jsonb_array_elements_text($4::text::jsonb) ), updated_at = now() where project_id = $1 and environment = $2 and secret_ref = $3