Skip to content

Console crashes with "crypto.randomUUID is not a function" on insecure origins (HTTP + LAN IP) #4563

Description

@yinlianghui

Summary

The Console SPA crashes with crypto.randomUUID is not a function when served over plain HTTP from any host other than localhost — e.g. accessing a dev server from another machine on the LAN via http://192.168.x.x:4001/_console/.

Root cause

crypto.randomUUID is only exposed in secure contexts (HTTPS, or http://localhost). On http://<LAN-IP>:<port> the browser simply does not provide the function, and the Console's list views crash into the ErrorBoundary ("出错了 / Something went wrong").

Repro

  1. objectstack dev (or pnpm dev on a framework app) on machine A, port 4001.
  2. Add machine A's LAN origin to OS_TRUSTED_ORIGINS so login passes CSRF.
  3. From machine B (or machine A itself), open http://<machine-A-LAN-IP>:4001/_console/, log in, open any object list view.
  4. List area renders the error boundary; browser console shows:
TypeError: crypto.randomUUID is not a function
    at .../_console/assets/vendor-react-....js
    componentStack: .../_console/assets/plugin-grid-....js

Observed with @objectstack/console@17.0.0-rc.6 (vendored dist). window.isSecureContext === false on that origin confirms the context.

Suggested fix

Guard/polyfill once, early (e.g. in index.html next to the existing window.process shim, or a shared util), falling back to crypto.getRandomValues:

<script>
  if (window.crypto && !window.crypto.randomUUID) {
    window.crypto.randomUUID = function () {
      var b = window.crypto.getRandomValues(new Uint8Array(16));
      b[6] = (b[6] & 0x0f) | 0x40;
      b[8] = (b[8] & 0x3f) | 0x80;
      var h = Array.prototype.map.call(b, function (x) {
        return x.toString(16).padStart(2, '0');
      }).join('');
      return h.slice(0, 8) + '-' + h.slice(8, 12) + '-' + h.slice(12, 16) + '-' + h.slice(16, 20) + '-' + h.slice(20);
    };
  }
</script>

We verified this exact shim (applied via pnpm patch on the vendored @objectstack/console dist) fixes the crash — list views render normally from a LAN-IP origin afterwards.

LAN-IP access to a dev box is a very common flow for demos / testing from a second device, so a built-in fallback would save every downstream app from patching this individually.

Activity

  1. self-assigned this
    on Aug 13, 2026
  2. yinlianghui commented on Aug 13, 2026

    @yinlianghui
    CollaboratorAuthor

    CLAIM — session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4563-insecure-origin-uuid. Dispatching a dev agent now.

    PM ruling (delegated decision authority; maintainer veto window open — record objections here): the crash's own stack decides the shape:

    1. The fix must be the EARLY GLOBAL shim, not a shared util — the TypeError fires inside the vendored react bundle, i.e. a DEPENDENCY calls crypto.randomUUID; no in-repo wrapper can reach that call site. Install the shim at the console app's entry (next to the existing window.process shim precedent the filer names), guarded on ABSENCE (!('randomUUID' in crypto)) so a native implementation is never overridden, generating RFC-4122 v4 via crypto.getRandomValues (the filer's verified patch is the reference — measure it against their pnpm-patch text if reachable, else the MDN-standard construction with version/variant bits).
    2. Census first: every in-repo crypto.randomUUID call site (expect plugin-grid + others) — they stay as-is once the global is guaranteed; note the census in the PR. If the census finds an objectui-side call that can run BEFORE the app entry's shim (e.g. module-eval time in a package loaded by a non-console host), report it — the shim's placement must precede every consumer.
    3. Tests: unit-test the generator (UUID shape, version nibble 4, variant bits, uniqueness sample) and the installer (deleting crypto.randomUUID then installing makes a consumer path work; native presence leaves the platform untouched). Red-first: with randomUUID deleted and no shim, the consumer path throws the card's exact TypeError.
    4. Changeset: per the touched package(s)' measured .d.ts — the console app is likely unpublished (measure; app-level shim ⇒ no changeset per the presence gate's verdict); a shared generator util, if you need to export one for tests, grades per its home. Never major.

    Mutual exclusion: apps/console + its entry are untouched by all in-flight cards (#4548 react+dashboard/report, #4566 core+DatasetWidget, #4567 StudioDesignSurface). Note #4570 (the SETTINGS_CRYPTO_UNAVAILABLE refusal rendering) is a SEPARATE pooled card — do not absorb it; if your census touches its surface, stop at the boundary.


    Generated by Claude Code


    Generated by Claude Code

  3. added 2 commits that reference this issue on Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions