diff --git a/.changeset/remove-uuid-dep.md b/.changeset/remove-uuid-dep.md new file mode 100644 index 0000000..3c33cbe --- /dev/null +++ b/.changeset/remove-uuid-dep.md @@ -0,0 +1,5 @@ +--- +'@statelyai/inspect': patch +--- + +Remove the `uuid` dependency in favor of the platform-native `crypto.randomUUID()`, clearing CVE-2026-41907 flagged by downstream security scanners. A `#uuid` subpath import resolves to `node:crypto` in Node and the global `crypto` in browser/worker environments. diff --git a/package.json b/package.json index 879fc9d..3f627a5 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,15 @@ "partysocket": "^0.0.25", "safe-stable-stringify": "^2.5.0", "superjson": "^1.13.3", - "uuid": "^9.0.1", "ws": "^8.20.0" }, + "imports": { + "#uuid": { + "worker": "./dist/uuid-browser.mjs", + "browser": "./dist/uuid-browser.mjs", + "default": "./dist/uuid-node.mjs" + } + }, "peerDependencies": { "xstate": "^5.5.1" }, @@ -46,7 +52,6 @@ "@changesets/changelog-github": "^0.5.2", "@changesets/cli": "^2.29.8", "@types/jsdom": "^21.1.7", - "@types/uuid": "^9.0.8", "@types/ws": "^8.18.1", "jsdom": "^23.2.0", "publint": "^0.3.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2a2709..ea1f3b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,6 @@ importers: superjson: specifier: ^1.13.3 version: 1.13.3 - uuid: - specifier: ^9.0.1 - version: 9.0.1 ws: specifier: ^8.20.0 version: 8.20.0 @@ -39,9 +36,6 @@ importers: '@types/jsdom': specifier: ^21.1.7 version: 21.1.7 - '@types/uuid': - specifier: ^9.0.8 - version: 9.0.8 '@types/ws': specifier: ^8.18.1 version: 8.18.1 @@ -737,9 +731,6 @@ packages: '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==, tarball: https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz} - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==, tarball: https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz} - '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==, tarball: https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz} @@ -1629,10 +1620,6 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==, tarball: https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz} - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==, tarball: https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz} - hasBin: true - vite-node@1.6.1: resolution: {integrity: sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==, tarball: https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz} engines: {node: ^18.0.0 || >=20.0.0} @@ -2325,8 +2312,6 @@ snapshots: '@types/tough-cookie@4.0.5': {} - '@types/uuid@9.0.8': {} - '@types/ws@8.18.1': dependencies: '@types/node': 25.5.2 @@ -3220,8 +3205,6 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - uuid@9.0.1: {} - vite-node@1.6.1(@types/node@25.5.2): dependencies: cac: 6.7.14 diff --git a/src/createSkyInspector.ts b/src/createSkyInspector.ts index cb45cfe..3c31a50 100644 --- a/src/createSkyInspector.ts +++ b/src/createSkyInspector.ts @@ -1,7 +1,7 @@ import IsomorphicWebSocket from 'isomorphic-ws'; import PartySocket from 'partysocket'; import { stringify } from 'superjson'; -import { v4 as uuidv4 } from 'uuid'; +import { uuid } from '#uuid'; import { createBrowserInspector } from './browser'; import { InspectorOptions, @@ -28,7 +28,7 @@ export function createSkyInspector( }; const server = apiBaseURL.replace('/api/sky', ''); const { apiKey, onerror, ...inspectorOptions } = options; - const sessionId = uuidv4(); // Generate a unique session ID + const sessionId = uuid(); // Generate a unique session ID const room = `inspect-${sessionId}`; const socket = new PartySocket({ host, diff --git a/src/uuid-browser.ts b/src/uuid-browser.ts new file mode 100644 index 0000000..6de6976 --- /dev/null +++ b/src/uuid-browser.ts @@ -0,0 +1,3 @@ +export function uuid(): string { + return crypto.randomUUID(); +} diff --git a/src/uuid-node.ts b/src/uuid-node.ts new file mode 100644 index 0000000..1fa5cba --- /dev/null +++ b/src/uuid-node.ts @@ -0,0 +1 @@ +export { randomUUID as uuid } from 'node:crypto'; diff --git a/tsconfig.json b/tsconfig.json index e8c2738..37e413b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,9 @@ "moduleDetection": "force", "module": "preserve", "moduleResolution": "bundler", + "paths": { + "#uuid": ["./src/uuid-node.ts"] + }, "resolveJsonModule": true, "strict": true, "esModuleInterop": true, diff --git a/tsdown.config.ts b/tsdown.config.ts index 45d2e07..4a6ebf5 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -1,5 +1,14 @@ import { defineConfig } from 'tsdown'; export default defineConfig({ - entry: ['src/index.ts', 'src/server.ts'], + entry: [ + 'src/index.ts', + 'src/server.ts', + 'src/uuid-node.ts', + 'src/uuid-browser.ts', + ], + // Keep the `#uuid` subpath external so the bare import survives in the + // bundle and the consumer's runtime/bundler resolves the right condition + // (browser/worker vs. node) instead of inlining one variant at build time. + external: ['#uuid'], }); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..5f6df9c --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,12 @@ +import { fileURLToPath } from 'node:url'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + resolve: { + alias: { + // Tests run against `src` before any build, so resolve the `#uuid` + // subpath (which points at `dist` for published consumers) to source. + '#uuid': fileURLToPath(new URL('./src/uuid-node.ts', import.meta.url)), + }, + }, +});