The provider UUID was generated once, stored in extension storage, and announced verbatim to every page on every load and across restarts, so any site — connected or not — could read a stable cross-site, cross-session identifier for the install: a supercookie contradicting the "no tracking" promise. EIP-6963 wants a fresh UUIDv4 per announcement instead. inpage.js now announces a per-load crypto.randomUUID() and persists nothing; the eip6963Uuid storage key and the AUTISTMASK_PROVIDER_UUID content-script message are removed. That key was a standalone top-level storage entry, never part of the versioned autistmask profile, so stateSchema.js and the persisted-field harness are untouched and no existing profile is affected. A jest test asserts two loads announce different UUIDv4s and that one load reuses a single UUID across re-announcements. Model: opus-4-8
104 lines
3.3 KiB
JavaScript
104 lines
3.3 KiB
JavaScript
// 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);
|
|
});
|
|
});
|