From 7f00aafdd66cdb997a5529b790b09228ed4ef917 Mon Sep 17 00:00:00 2001 From: Nitish Reddy M Date: Thu, 27 Aug 2026 21:31:38 -0400 Subject: [PATCH] test(utils): cover generateId's insecure-context fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #3393 The crash reported there (crypto.randomUUID is not a function, hit on self-hosted deployments served over plain HTTP — browsers only expose crypto.randomUUID() in secure contexts) is already fixed on staging: generateId() has feature-detected crypto.randomUUID and fallen back to a crypto.getRandomValues()-based UUID v4 since b5674d9ed4 (#4228), and no client-side code calls crypto.randomUUID directly anymore (enforced by scripts/check-utils-enforcement.ts). What was missing: the existing test suite never exercised the fallback branch, since Node/Bun's crypto.randomUUID is always present in the test environment. A future refactor could silently break the exact path that fixes #3393 with nothing catching it. Adds two tests that stub crypto.randomUUID to undefined (restored in afterEach) and assert the fallback still produces valid, unique UUID v4s. Verified these tests actually fail with the pre-fix TypeError when generateId() is reverted to a naive crypto.randomUUID() call. Co-Authored-By: Claude Sonnet 5 --- packages/utils/src/id.test.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/id.test.ts b/packages/utils/src/id.test.ts index 262115b3bb0..1324d35f2d3 100644 --- a/packages/utils/src/id.test.ts +++ b/packages/utils/src/id.test.ts @@ -1,12 +1,18 @@ /** * @vitest-environment node */ -import { describe, expect, it } from 'vitest' +import { afterEach, describe, expect, it } from 'vitest' import { generateId, generateShortId, isValidUuid } from './id.js' const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i describe('generateId', () => { + const originalRandomUUID = crypto.randomUUID + + afterEach(() => { + crypto.randomUUID = originalRandomUUID + }) + it('returns a valid UUID v4', () => { const id = generateId() expect(id).toMatch(UUID_V4_RE) @@ -16,6 +22,23 @@ describe('generateId', () => { const ids = new Set(Array.from({ length: 100 }, () => generateId())) expect(ids.size).toBe(100) }) + + it('falls back to crypto.getRandomValues when randomUUID is unavailable', () => { + // Simulates a browser insecure context (plain-HTTP self-hosted deployment, + // e.g. issue #3393): `crypto.randomUUID` is undefined there, and the + // pre-fix code threw `TypeError: crypto.randomUUID is not a function`. + // @ts-expect-error simulating a runtime without crypto.randomUUID + crypto.randomUUID = undefined + const id = generateId() + expect(id).toMatch(UUID_V4_RE) + }) + + it('returns unique fallback values across 100 calls', () => { + // @ts-expect-error simulating a runtime without crypto.randomUUID + crypto.randomUUID = undefined + const ids = new Set(Array.from({ length: 100 }, () => generateId())) + expect(ids.size).toBe(100) + }) }) describe('generateShortId', () => {