From 96f5c653c20d9ac325d00f1f52624ca6a373ff8f Mon Sep 17 00:00:00 2001 From: thephez Date: Tue, 12 May 2026 12:04:28 -0400 Subject: [PATCH 1/2] fix(dashproof-lab): show clear error when crypto.subtle is unavailable Hashing requires a Secure Context, so plain http:// origins (e.g. bare-IP hosting, non-HTTPS IPFS gateways) crashed with "Cannot read properties of undefined (reading 'digest')". Detect the missing crypto.subtle up front and throw a message pointing to HTTPS or localhost. Applied to both the React app and the dashproof-lite single-file companion. Co-Authored-By: Claude Opus 4.7 (1M context) --- example-apps/dashproof-lab/public/dashproof-lite.html | 5 +++++ example-apps/dashproof-lab/src/lib/hash.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/example-apps/dashproof-lab/public/dashproof-lite.html b/example-apps/dashproof-lab/public/dashproof-lite.html index ef53352..a9e10f5 100644 --- a/example-apps/dashproof-lab/public/dashproof-lite.html +++ b/example-apps/dashproof-lab/public/dashproof-lite.html @@ -217,6 +217,11 @@

History by chainId

// SHA-256 via WebCrypto. Runs entirely in the browser; the file never // leaves the page. async function hashFile(file) { + if (typeof crypto === 'undefined' || !crypto.subtle) { + throw new Error( + 'SHA-256 hashing requires a secure context. Open this page over HTTPS or via http://localhost — the browser disables crypto.subtle on plain http:// origins.', + ); + } const buf = await file.arrayBuffer(); return new Uint8Array(await crypto.subtle.digest('SHA-256', buf)); } diff --git a/example-apps/dashproof-lab/src/lib/hash.ts b/example-apps/dashproof-lab/src/lib/hash.ts index d8e4353..384f4fb 100644 --- a/example-apps/dashproof-lab/src/lib/hash.ts +++ b/example-apps/dashproof-lab/src/lib/hash.ts @@ -1,4 +1,9 @@ export async function hashFile(file: File): Promise { + if (typeof crypto === "undefined" || !crypto.subtle) { + throw new Error( + "SHA-256 hashing requires a secure context. Open this app over HTTPS or via http://localhost — the browser disables crypto.subtle on plain http:// origins.", + ); + } const buffer = typeof file.arrayBuffer === "function" ? await file.arrayBuffer() From 87322ceda3baa85dedebd87552d00e06e6ac10e1 Mon Sep 17 00:00:00 2001 From: thephez Date: Tue, 12 May 2026 12:07:43 -0400 Subject: [PATCH 2/2] test(dashproof-lab): cover Secure Context guard in hashFile Asserts the exact error message so an accidental rewording would fail the test instead of silently degrading the user-facing diagnostic. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dashproof-lab/test/hashFile.test.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/example-apps/dashproof-lab/test/hashFile.test.ts b/example-apps/dashproof-lab/test/hashFile.test.ts index 5c93395..2a20095 100644 --- a/example-apps/dashproof-lab/test/hashFile.test.ts +++ b/example-apps/dashproof-lab/test/hashFile.test.ts @@ -3,11 +3,21 @@ // which would make this regression test useless. Node 20+ provides a working // global File backed by Undici's Blob implementation. -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; import { bytesToHex, hashFile } from "../src/lib/hash"; describe("hashFile", () => { + const originalSubtle = globalThis.crypto?.subtle; + afterEach(() => { + if (globalThis.crypto) { + Object.defineProperty(globalThis.crypto, "subtle", { + configurable: true, + value: originalSubtle, + }); + } + }); + it("hashes binary (non-UTF-8) bytes correctly", async () => { // 0xFF 0xFE is invalid UTF-8 — a TextEncoder-based fallback would // corrupt these bytes into the U+FFFD replacement character before @@ -30,4 +40,15 @@ describe("hashFile", () => { "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ); }); + + it("throws a clear Secure Context error when crypto.subtle is missing", async () => { + Object.defineProperty(globalThis.crypto, "subtle", { + configurable: true, + value: undefined, + }); + const file = new File([Uint8Array.from([0x00])], "x.bin"); + await expect(hashFile(file)).rejects.toThrow( + "SHA-256 hashing requires a secure context. Open this app over HTTPS or via http://localhost — the browser disables crypto.subtle on plain http:// origins.", + ); + }); });