From afba4cdaa65b701575345d6c308bc8b2d9d02eb1 Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Tue, 30 Jun 2026 00:13:52 +0000 Subject: [PATCH] [Security] Harden client ID generation in ExtensionServerClient Replace insecure Math.random() with globalThis.crypto.randomUUID() for generating client IDs. Included fallback for environments without Web Crypto API and added regression tests. --- .../ExtensionServerClient.test.ts | 22 +++++++++++++++++++ .../ExtensionServerClient.ts | 6 ++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.test.ts b/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.test.ts index 8ef4ffa1aea..d12ef00ed29 100644 --- a/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.test.ts +++ b/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.test.ts @@ -188,6 +188,28 @@ describe('ExtensionServerClient', () => { expect(client.connection).toBeUndefined() expect(mockSocketServer.clients.length).toBe(0) }) + + test('uses crypto.randomUUID for id generation when available', () => { + const uuid = '12345678-1234-1234-1234-123456789012' + vi.stubGlobal('crypto', { + randomUUID: () => uuid, + }) + + const client = new ExtensionServerClient() + expect(client.id).toBe(uuid) + + vi.unstubAllGlobals() + }) + + test('falls back to Math.random for id generation when crypto.randomUUID is not available', () => { + vi.stubGlobal('crypto', undefined) + + const client = new ExtensionServerClient() + expect(client.id).toBeDefined() + expect(client.id.length).toBeGreaterThan(0) + + vi.unstubAllGlobals() + }) }) describe('on()', () => { diff --git a/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.ts b/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.ts index 507bfb5df73..4a568d8a23d 100644 --- a/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.ts +++ b/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.ts @@ -32,7 +32,11 @@ export class ExtensionServerClient implements ExtensionServer.Client { private uiExtensionsByUuid: Record = {} constructor(options: DeepPartial = {}) { - this.id = (Math.random() + 1).toString(36).substring(7) + // We use a CSPRNG for ID generation where available to prevent predictability. + this.id = + typeof globalThis.crypto?.randomUUID === 'function' + ? globalThis.crypto.randomUUID() + : (Math.random() + 1).toString(36).substring(7) this.options = getValidatedOptions({ ...options, connection: {