diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 9799df421c31..c8afc0818909 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -31,7 +31,15 @@ export function uuid4(): string { return crypto.randomUUID().replace(/-/g, ''); } if (crypto && crypto.getRandomValues) { - getRandomByte = () => crypto.getRandomValues(new Uint8Array(1))[0]; + getRandomByte = () => { + // crypto.getRandomValues might return undefined instead of the typed array + // in old Chromium versions (e.g. 23.0.1235.0 (151422)) + // However, `typedArray` is still filled in-place. + // @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#typedarray + const typedArray = new Uint8Array(1); + crypto.getRandomValues(typedArray); + return typedArray[0]; + }; } } catch (_) { // some runtimes can crash invoking crypto diff --git a/packages/utils/test/misc.test.ts b/packages/utils/test/misc.test.ts index dc75b70d4286..c1eb978dcdbe 100644 --- a/packages/utils/test/misc.test.ts +++ b/packages/utils/test/misc.test.ts @@ -343,6 +343,25 @@ describe('uuid4 generation', () => { expect(uuid4()).toMatch(uuid4Regex); } }); + + // Corner case related to crypto.getRandomValues being only + // semi-implemented (e.g. Chromium 23.0.1235.0 (151422)) + it('returns valid uuid v4 even if crypto.getRandomValues does not return a typed array', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const cryptoMod = require('crypto'); + + const getRandomValues = (typedArray: Uint8Array) => { + if (cryptoMod.getRandomValues) { + cryptoMod.getRandomValues(typedArray); + } + }; + + (global as any).crypto = { getRandomValues }; + + for (let index = 0; index < 1_000; index++) { + expect(uuid4()).toMatch(uuid4Regex); + } + }); }); describe('arrayify()', () => {