mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
crypto.randomUUID() is [SecureContext] in the Web Cryptography IDL, so a page served over plain HTTP never gets it. Measured in Chromium against http://<lan-ip>: isSecureContext false, randomUUID and subtle undefined, getRandomValues still a function. The document vault minted its queue id and Idempotency-Key that way, inside the file-input change handler. On a LAN self-host the call threw before a queue row existed, so picking a file produced no upload, no error and no trace — the picker simply looked inert, and the only way through was to reach the app over HTTPS. The client transport has minted a default Idempotency-Key the same way since v1.37.18, which put every apiPost and apiPatch on the same footing: on such a host nothing wrote at all. Add randomId(), which prefers the platform call and otherwise assembles a v4 UUID from getRandomValues — the same CSPRNG, the same 122 bits, no secure context required — and route every browser-side call site through it. Nothing in an upload needs one: the bytes travel over multipart XHR and every cryptographic operation happens on the server. Passkeys, web push and home-screen install still need HTTPS; browsers withhold those outright. A structural guard fails a direct crypto.randomUUID() anywhere outside the two server-side files that may keep it, so the class cannot return one call site at a time. Also point the CSV and JSON import cards straight at the import guide. They resolved through a redirect under the integrations tree, which is the wrong place for a guide that is not an integration runbook. The suite that covered those links pinned the old spelling rather than the guarantee; it now asserts an absolute docs-site URL, no internal path, and new-tab hygiene.
56 lines
2.2 KiB
TypeScript
56 lines
2.2 KiB
TypeScript
/**
|
|
* A random v4 UUID that also works on a plain-HTTP origin.
|
|
*
|
|
* `crypto.randomUUID()` is marked `[SecureContext]` in the Web Cryptography
|
|
* IDL, so on a LAN self-host served over plain HTTP the property is simply
|
|
* absent and the call throws `TypeError: crypto.randomUUID is not a
|
|
* function`. Measured in Chromium on `http://<lan-ip>`: `isSecureContext`
|
|
* false, `crypto.randomUUID` and `crypto.subtle` undefined — but
|
|
* `crypto.getRandomValues` still a function, because it carries no
|
|
* `[SecureContext]` marker.
|
|
*
|
|
* That threw where nobody caught it. The document-vault upload minted a
|
|
* queue id and an `Idempotency-Key` this way inside the file-input change
|
|
* handler, so picking a file on a plain-HTTP host did nothing at all: no
|
|
* queue row, no request, no error. Since the client transport started
|
|
* minting a default `Idempotency-Key`, every `apiPost` / `apiPatch` in the
|
|
* app failed the same way on such a host.
|
|
*
|
|
* So the id is built from `getRandomValues` whenever `randomUUID` is out of
|
|
* reach. Same 122 bits of entropy from the same CSPRNG, same RFC 4122
|
|
* version-4 layout, no secure context required. Nothing in an upload needs
|
|
* one: the bytes go out over multipart XHR and every cryptographic
|
|
* operation happens on the server.
|
|
*/
|
|
|
|
/** Hex lookup for the byte→string step; built once. */
|
|
const HEX = Array.from({ length: 256 }, (_, i) =>
|
|
i.toString(16).padStart(2, "0"),
|
|
);
|
|
|
|
/**
|
|
* A fresh RFC 4122 version-4 UUID.
|
|
*
|
|
* Prefers the platform's own `randomUUID` and falls back to the
|
|
* always-available `getRandomValues`. Use this instead of
|
|
* `crypto.randomUUID()` in anything that runs in the browser — the
|
|
* `client-random-id-guard` test fails a direct call.
|
|
*/
|
|
export function randomId(): string {
|
|
if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
|
|
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
// RFC 4122 §4.4: version nibble to 4, variant bits to 0b10.
|
|
bytes[6] = (bytes[6]! & 0x0f) | 0x40;
|
|
bytes[8] = (bytes[8]! & 0x3f) | 0x80;
|
|
|
|
const hex = Array.from(bytes, (b) => HEX[b]!).join("");
|
|
return [
|
|
hex.slice(0, 8),
|
|
hex.slice(8, 12),
|
|
hex.slice(12, 16),
|
|
hex.slice(16, 20),
|
|
hex.slice(20),
|
|
].join("-");
|
|
}
|