diff --git a/client/config.js b/client/config.js index ec7bc023..2980a697 100644 --- a/client/config.js +++ b/client/config.js @@ -17,6 +17,8 @@ */ const BUILD = "6f59094d60f98fafc14371671d3ff31ef4d75d9e"; +// Update this SHA-512 pin whenever BUILD changes. +const BUILD_INTEGRITY = "sha512-wMsIkGuT5cbZoZqnzqJIRXmTOE1Tn4KP/XdiF40XobfJ6XVrgaiQNduF5USHoaut5p+X6a5rswWq8O4Ia9qXRw=="; const CDN = "https://static.diep.io/"; const API_URL = `${window.location.href}api/`; diff --git a/client/loader.js b/client/loader.js index f6f000e0..94e5a9c6 100644 --- a/client/loader.js +++ b/client/loader.js @@ -337,7 +337,58 @@ Module.todo.push([() => { Module.status = "FETCH"; // fetch necessary info and build return [ - fetch(`${CDN}build_${BUILD}.wasm.wasm`).then(res => res.arrayBuffer()), + (async () => { + const url = `${CDN}build_${BUILD}.wasm.wasm`; + let cache; + try { + cache = await caches.open("diepcustom-wasm"); + const hit = await cache.match(url); + if (hit) return await hit.arrayBuffer(); + } catch (error) { console.warn("WASM cache unavailable", error); } + + // A sandboxed frame sends Origin: null instead of the site's origin. + const buffer = await new Promise((resolve, reject) => { + const frame = document.createElement("iframe"); + frame.hidden = true; + frame.setAttribute("sandbox", "allow-scripts"); + const finish = data => { + clearTimeout(timer); + window.removeEventListener("message", onMessage); + frame.remove(); + if (data instanceof ArrayBuffer) resolve(data); + else { + alert("Failed to load or verify the WASM build. Check the console for details."); + reject(new Error(data?.error || "WASM fetch failed")); + } + }; + const onMessage = event => { + if (event.source === frame.contentWindow && event.origin === "null") finish(event.data); + }; + const timer = setTimeout(() => finish({ error: "WASM fetch timed out" }), 60000); + window.addEventListener("message", onMessage); + frame.srcdoc = `