From 9a535cff4c0124868a10ad7b92842ed916d8c6a6 Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Mon, 29 Jun 2026 00:39:45 +0000 Subject: [PATCH] [Security] Harden client ID generation Replace insecure Math.random() with globalThis.crypto.randomUUID() for client ID generation in ExtensionServerClient. This ensures that IDs are cryptographically secure and non-predictable when the environment supports the Web Crypto API. Added a fallback to the original logic for compatibility. --- .../ExtensionServerClient.test.ts | 24 +++++++++++++++++++ .../ExtensionServerClient.ts | 6 ++++- 2 files changed, 29 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..5473de27478 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,30 @@ describe('ExtensionServerClient', () => { expect(client.connection).toBeUndefined() expect(mockSocketServer.clients.length).toBe(0) }) + + test('generates a random UUID for the client id using crypto.randomUUID when available', () => { + const uuid = '12345678-1234-1234-1234-123456789012' + const randomUUIDSpy = vi.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue(uuid) + + const client = new ExtensionServerClient() + + expect(client.id).toBe(uuid) + expect(randomUUIDSpy).toHaveBeenCalled() + + randomUUIDSpy.mockRestore() + }) + + test('falls back to Math.random for the client id when crypto.randomUUID is not available', () => { + const originalCrypto = globalThis.crypto + delete (globalThis as any).crypto + + const client = new ExtensionServerClient() + + expect(client.id).toBeDefined() + expect(client.id.length).toBeLessThanOrEqual(10) + + globalThis.crypto = originalCrypto + }) }) 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..913a1596430 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) + // Use a cryptographically secure random number generator if available to avoid predictable IDs + this.id = + typeof globalThis.crypto?.randomUUID === 'function' + ? globalThis.crypto.randomUUID() + : (Math.random() + 1).toString(36).substring(7) this.options = getValidatedOptions({ ...options, connection: {