Files
AutistMask/tests/rpcOrigin.test.js
T
sneak 9adae6c9c7
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run
fix: a page with an opaque origin is refused, not treated as a site (closes #527)
A page with an opaque origin, such as one its server sandboxes with a
Content-Security-Policy, is reported to the background with the origin
"null". The background took that as a site, so every such page on every
website was one site: a connection remembered for one covered them all,
and the phishing check never matched. A request whose origin is "null" is
now refused with 4100, like one with no origin, before any method runs,
and is not credited to the site its URL names. tests/rpcOrigin.test.js
sends one, and README's SiteApproval says so.

Model: opus-5-5
2026-10-14 18:31:16 +00:00

173 lines
5.4 KiB
JavaScript

// Which site a page's request is attributed to.
//
// The background takes a request's origin from what the browser says sent the
// message: sender.origin, or on Firefox before 126, which has no
// sender.origin, the origin of sender.url — the frame that sent it. It used to
// fall back to the tab's page and then to an origin the message itself
// carried, so a request from a frame was credited to the site embedding it,
// and a request the browser said nothing about was credited to whatever the
// page wrote (https://git.eeqj.de/sneak/AutistMask/issues/407).
//
// A page with an opaque origin, such as one its server sandboxes with a
// Content-Security-Policy, is reported with the origin "null". That names no
// site, so its request is refused
// (https://git.eeqj.de/sneak/AutistMask/issues/527).
//
// Every other sender here lacks sender.origin, as on old Firefox. The
// connection check on eth_accounts is what shows which site a request was
// credited to.
const { makeStorageStub } = require("./support/storageStub");
const ADDRESS = "0x66133E8ea0f5D1d612D2502a968757D1048c214a";
// The site the persisted state has connected, and one it has never heard of.
const CONNECTED_ORIGIN = "https://dapp.example";
const STRANGER_ORIGIN = "https://stranger.example";
async function settle() {
for (let i = 0; i < 50; i++) await Promise.resolve();
}
afterEach(() => {
delete global.chrome;
});
function loadBackground() {
jest.resetModules();
jest.doMock("../src/shared/balances", () => ({
getProvider: () => ({}),
refreshBalances: jest.fn(async () => {}),
}));
jest.doMock("../src/shared/phishingDomains", () => ({
isPhishingDomain: () => false,
}));
jest.doMock("../src/shared/alarms", () => ({
BALANCE_REFRESH_ALARM: "balance",
BALANCE_REFRESH_PERIOD_MINUTES: 1,
ensureRecurringAlarms: jest.fn(async () => {}),
registerAlarmHandlers: jest.fn(),
}));
const storage = makeStorageStub({
autistmask: {
networkId: "mainnet",
wallets: [
{
name: "Wallet 1",
type: "hd",
addresses: [
{ address: ADDRESS, balance: "0", tokenBalances: [] },
],
},
],
activeAddress: ADDRESS,
allowedSites: { [ADDRESS]: [CONNECTED_ORIGIN] },
deniedSites: {},
},
});
let messageListener = null;
global.chrome = {
storage,
runtime: {
getURL: (path) => "chrome-extension://autistmask/" + path,
onMessage: {
addListener: (fn) => {
messageListener = fn;
},
},
onConnect: { addListener: () => {} },
lastError: null,
},
windows: { onRemoved: { addListener: () => {} } },
action: { setPopup: () => {} },
};
require("../src/background/index");
// Ask for eth_accounts. `claimedOrigin` is an origin written into the
// message, as the content script used to send.
return async function accounts(sender, claimedOrigin) {
let result = null;
messageListener(
{
type: "AUTISTMASK_RPC",
method: "eth_accounts",
params: [],
origin: claimedOrigin,
},
sender,
(r) => {
result = r;
},
);
await settle();
return result;
};
}
describe("a request is attributed to the frame that sent it", () => {
test("a stranger's frame on a connected site gets no address", async () => {
const accounts = loadBackground();
const result = await accounts({
url: STRANGER_ORIGIN + "/frame.html",
tab: { url: CONNECTED_ORIGIN + "/" },
});
expect(result).toEqual({ result: [] });
});
test("a connected site's frame on a stranger's page gets the address", async () => {
const accounts = loadBackground();
const result = await accounts({
url: CONNECTED_ORIGIN + "/frame.html",
tab: { url: STRANGER_ORIGIN + "/" },
});
expect(result).toEqual({ result: [ADDRESS] });
});
});
describe("a request the browser does not say the sender of", () => {
test("is refused, whatever the tab or the message says", async () => {
const accounts = loadBackground();
const result = await accounts(
{ tab: { url: CONNECTED_ORIGIN + "/" } },
CONNECTED_ORIGIN,
);
expect(result).toEqual({
error: {
code: 4100,
message:
"The wallet could not tell which site sent this request.",
},
});
});
});
describe("a request from a page with an opaque origin", () => {
test("is refused, whatever the page's URL says", async () => {
const accounts = loadBackground();
const result = await accounts({
origin: "null",
url: CONNECTED_ORIGIN + "/sandboxed.html",
tab: { url: CONNECTED_ORIGIN + "/sandboxed.html" },
});
expect(result).toEqual({
error: {
code: 4100,
message:
"The wallet could not tell which site sent this request.",
},
});
});
});