// 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 asks for one UUIDv4 per page load, shared by every announcement in // that load. The fix generates it per page load and stores nothing. // // The stored UUID reached inpage.js as an AUTISTMASK_PROVIDER_UUID page // message from the content script, and inpage.js then announced it. Each load // below is posted the same stored UUID that way, so on the old code both loads // announce it and the last two tests fail. // // 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; } } // What the old content script read out of extension storage and posted to // every page load. const STORED_UUID = "11111111-2222-4333-8444-555555555555"; // Evaluate inpage.js once against a fresh stub window, post it STORED_UUID the // way the old content script did, then dispatch a `requestProvider` event so a // re-announcement is observed as well as the announcement at load. Returns // every UUID the load announced, in order. 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({ type: "message", source: win, data: { type: "AUTISTMASK_PROVIDER_UUID", uuid: STORED_UUID }, }); 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", () => { 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, neither the stored one", () => { const first = announcedUuids(); const second = announcedUuids(); expect(first).not.toContain(STORED_UUID); expect(second).not.toContain(STORED_UUID); expect(second[0]).not.toBe(first[0]); }); });