Skip to content

fix(core): don't rely on crypto.randomUUID for asset ids - #681

Open
maherm wants to merge 1 commit into
pascalorg:mainfrom
maherm:fix/asset-storage-insecure-context
Open

fix(core): don't rely on crypto.randomUUID for asset ids#681
maherm wants to merge 1 commit into
pascalorg:mainfrom
maherm:fix/asset-storage-insecure-context

Conversation

@maherm

@maherm maherm commented Aug 18, 2026

Copy link
Copy Markdown

Summary

saveAsset() (called on every file upload/drop, from reference-panel.tsx and local-guide-image.ts) used crypto.randomUUID() to generate the IndexedDB key. That method requires a secure context (HTTPS or localhost) and is undefined otherwise, so it throws on plain-HTTP origins — e.g. an internal-network deployment reached over a bare IP or hostname. Every upload fails immediately there.

Switched to nanoid's customAlphabet, which has no secure-context requirement and is already used for ids elsewhere in this package (schema/base.ts). I preferred that over the narrower typeof-check + counter fallback in packages/nodes/src/cabinet/stack.ts: that counter resets to 0 on every reload, which is fine for stack.ts's transient in-memory compartment ids but not here — asset ids are persisted as IndexedDB keys and as asset://<id> references inside saved/exported scenes, so a colliding fallback could silently overwrite a different asset. nanoid gives the same collision resistance on every code path instead of degrading on insecure origins.

Test plan

  • Added a regression test in packages/core/src/lib/asset-storage.test.ts. There was no existing test for this module because idb-keyval (used here) needs a real indexedDB global, which Bun's test runtime doesn't provide — added fake-indexeddb as a devDependency to fill that gap.
  • The new test stubs crypto.randomUUID as unavailable and asserts saveAsset()/loadAssetUrl() still round-trip correctly. Confirmed it fails against the old implementation (TypeError: crypto.randomUUID is not a function) and passes with the fix.
  • Full packages/core suite (957 tests) passes.
  • bun run check (biome) clean across the repo.

Note

Low Risk
Small, localized ID generation change with regression tests; no auth or API surface changes, though persisted asset:// id format shifts from UUID to nanoid strings.

Overview
Fixes file uploads on plain-HTTP deployments by changing how persisted asset IDs are generated in saveAsset().

saveAsset() previously used crypto.randomUUID(), which is unavailable outside a secure context (HTTPS/localhost), so uploads from editor flows that call it could throw immediately on internal HTTP hosts. IDs are now produced with nanoid's customAlphabet (16-char alphanumeric), which works in insecure contexts and stays suitable for long-lived IndexedDB keys and asset:// references in saved scenes.

Test coverage for asset-storage is added via fake-indexeddb in Bun tests, including a regression that stubs crypto.randomUUID as missing and asserts save/load still round-trip.

Reviewed by Cursor Bugbot for commit cf5611b. Bugbot is set up for automated code reviews on this repo. Configure here.

saveAsset() (called from reference-panel.tsx and local-guide-image.ts,
i.e. on every file upload/drop) used crypto.randomUUID() to generate
the IndexedDB key. That method requires a secure context (HTTPS or
localhost) and is undefined otherwise, so it throws on plain-HTTP
origins such as an internal-network deployment reached over a bare
IP or hostname - every upload failed immediately there.

Switched to nanoid's customAlphabet, which has no secure-context
requirement and is already used for ids elsewhere in this package
(schema/base.ts). Preferred that over the narrower typeof-check +
counter fallback used in packages/nodes/src/cabinet/stack.ts: that
counter resets to 0 on every reload, which is fine for stack.ts's
transient in-memory compartment ids but not here - asset ids are
persisted as IndexedDB keys and as asset://<id> references inside
saved/exported scenes, so a colliding fallback could silently
overwrite a different asset. nanoid gives the same collision
resistance on every code path instead of degrading on insecure
origins.

Also adds a regression test. There was no existing test for this
module because idb-keyval (used here) needs a real indexedDB global,
which Bun's test runtime doesn't provide - added fake-indexeddb as a
devDependency to fill that gap. The new test stubs crypto.randomUUID
as unavailable and asserts saveAsset()/loadAssetUrl() still round-trip
correctly; confirmed it fails with the old implementation
(TypeError: crypto.randomUUID is not a function) and passes with the
fix. Full packages/core suite (957 tests) passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant