diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/init.js b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/init.js index 09af5f3e4ab4..d32f77f4cb6b 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/init.js @@ -5,6 +5,15 @@ window.Sentry = Sentry; // Force this so that the initial sampleRand is consistent Math.random = () => 0.45; +// Polyfill crypto.randomUUID +crypto.randomUUID = function randomUUID() { + return ([1e7] + 1e3 + 4e3 + 8e3 + 1e11).replace( + /[018]/g, + // eslint-disable-next-line no-bitwise + c => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16), + ); +}; + Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [Sentry.browserTracingIntegration()], diff --git a/packages/core/src/utils/misc.ts b/packages/core/src/utils/misc.ts index 607eff129fe5..69cd217345b8 100644 --- a/packages/core/src/utils/misc.ts +++ b/packages/core/src/utils/misc.ts @@ -7,7 +7,6 @@ import { snipLine } from './string'; import { GLOBAL_OBJ } from './worldwide'; interface CryptoInternal { - getRandomValues(array: Uint8Array): Uint8Array; randomUUID?(): string; } @@ -22,37 +21,34 @@ function getCrypto(): CryptoInternal | undefined { return gbl.crypto || gbl.msCrypto; } +let emptyUuid: string | undefined; + +function getRandomByte(): number { + return Math.random() * 16; +} + /** * UUID4 generator * @param crypto Object that provides the crypto API. * @returns string Generated UUID4. */ export function uuid4(crypto = getCrypto()): string { - let getRandomByte = (): number => Math.random() * 16; try { if (crypto?.randomUUID) { return crypto.randomUUID().replace(/-/g, ''); } - if (crypto?.getRandomValues) { - 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); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return typedArray[0]!; - }; - } } catch { // some runtimes can crash invoking crypto // https://github.com/getsentry/sentry-javascript/issues/8935 } - // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 - // Concatenating the following numbers as strings results in '10000000100040008000100000000000' - return (([1e7] as unknown as string) + 1e3 + 4e3 + 8e3 + 1e11).replace(/[018]/g, c => + if (!emptyUuid) { + // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 + // Concatenating the following numbers as strings results in '10000000100040008000100000000000' + emptyUuid = ([1e7] as unknown as string) + 1e3 + 4e3 + 8e3 + 1e11; + } + + return emptyUuid.replace(/[018]/g, c => // eslint-disable-next-line no-bitwise ((c as unknown as number) ^ ((getRandomByte() & 15) >> ((c as unknown as number) / 4))).toString(16), ); diff --git a/packages/core/test/lib/utils/misc.test.ts b/packages/core/test/lib/utils/misc.test.ts index 83e7f4c05b66..885e2dc64b8d 100644 --- a/packages/core/test/lib/utils/misc.test.ts +++ b/packages/core/test/lib/utils/misc.test.ts @@ -292,28 +292,21 @@ describe('checkOrSetAlreadyCaught()', () => { describe('uuid4 generation', () => { const uuid4Regex = /^[0-9A-F]{12}[4][0-9A-F]{3}[89AB][0-9A-F]{15}$/i; - it('returns valid uuid v4 ids via Math.random', () => { + it('returns valid and unique uuid v4 ids via Math.random', () => { + const uuids = new Set(); for (let index = 0; index < 1_000; index++) { - expect(uuid4()).toMatch(uuid4Regex); - } - }); - - it('returns valid uuid v4 ids via crypto.getRandomValues', () => { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const cryptoMod = require('crypto'); - - const crypto = { getRandomValues: cryptoMod.getRandomValues }; - - for (let index = 0; index < 1_000; index++) { - expect(uuid4(crypto)).toMatch(uuid4Regex); + const id = uuid4(); + expect(id).toMatch(uuid4Regex); + uuids.add(id); } + expect(uuids.size).toBe(1_000); }); it('returns valid uuid v4 ids via crypto.randomUUID', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const cryptoMod = require('crypto'); - const crypto = { getRandomValues: cryptoMod.getRandomValues, randomUUID: cryptoMod.randomUUID }; + const crypto = { randomUUID: cryptoMod.randomUUID }; for (let index = 0; index < 1_000; index++) { expect(uuid4(crypto)).toMatch(uuid4Regex); @@ -321,7 +314,7 @@ describe('uuid4 generation', () => { }); it("return valid uuid v4 even if crypto doesn't exists", () => { - const crypto = { getRandomValues: undefined, randomUUID: undefined }; + const crypto = { randomUUID: undefined }; for (let index = 0; index < 1_000; index++) { expect(uuid4(crypto)).toMatch(uuid4Regex); @@ -330,9 +323,6 @@ describe('uuid4 generation', () => { it('return valid uuid v4 even if crypto invoked causes an error', () => { const crypto = { - getRandomValues: () => { - throw new Error('yo'); - }, randomUUID: () => { throw new Error('yo'); }, @@ -342,25 +332,4 @@ describe('uuid4 generation', () => { expect(uuid4(crypto)).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); - } - }; - - const crypto = { getRandomValues }; - - for (let index = 0; index < 1_000; index++) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - we are testing a corner case - expect(uuid4(crypto)).toMatch(uuid4Regex); - } - }); });