// The EIP-6963 provider UUID inpage.js announces (src/content/inpage.js). // // The bug this pins down (issue #398): the UUID used to be generated once, // persisted in extension storage, and announced verbatim to every page on // every load and across browser restarts, so any site — connected or not — // could read a stable cross-site, cross-session identifier for the install. // EIP-6963 wants a fresh UUIDv4 per announcement instead. The fix generates // it per page load and stores nothing. // // inpage.js is a bare IIFE injected into the page's JS context, not a module; // see tests/inpageErrors.test.js for why it is evaluated against a stub window // rather than imported. Here the stub captures the CustomEvent that carries // the announcement, so the UUID this file reads is the one a real dApp's // eip6963:announceProvider listener would see. const fs = require("fs"); const path = require("path"); const { webcrypto } = require("crypto"); const SOURCE = fs.readFileSync( path.join(__dirname, "..", "src", "content", "inpage.js"), "utf8", ); const loadInto = new Function( "window", "self", "crypto", "Event", "CustomEvent", SOURCE, ); class StubEvent { constructor(type) { this.type = type; } } class StubCustomEvent extends StubEvent { constructor(type, init) { super(type); this.detail = init && init.detail; } } // Evaluate inpage.js once against a fresh stub window and return every UUID it // announced. A `requestProvider` event is dispatched too, so a re-announcement // within one load is observed as well as the announcement at load. function announcedUuids() { const listeners = {}; const uuids = []; const win = { addEventListener(type, fn) { (listeners[type] || (listeners[type] = [])).push(fn); }, removeEventListener(type, fn) { const fns = listeners[type]; if (!fns) return; const i = fns.indexOf(fn); if (i !== -1) fns.splice(i, 1); }, postMessage() {}, dispatchEvent(event) { if (event.type === "eip6963:announceProvider") { uuids.push(event.detail.info.uuid); } for (const fn of (listeners[event.type] || []).slice()) fn(event); return true; }, }; win.window = win; loadInto(win, win, webcrypto, StubEvent, StubCustomEvent); win.dispatchEvent(new StubEvent("eip6963:requestProvider")); return uuids; } const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; describe("the EIP-6963 provider UUID is fresh per page load", () => { test("a load announces a UUIDv4, unprompted, with nothing delivered", () => { const uuids = announcedUuids(); expect(uuids.length).toBeGreaterThan(0); expect(uuids[0]).toMatch(UUID_V4); }); test("every announcement within one load carries the same UUID", () => { const uuids = announcedUuids(); expect(uuids.length).toBeGreaterThan(1); expect(new Set(uuids).size).toBe(1); }); test("two page loads announce different UUIDs", () => { const first = announcedUuids()[0]; const second = announcedUuids()[0]; expect(first).toMatch(UUID_V4); expect(second).toMatch(UUID_V4); expect(second).not.toBe(first); }); });