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", and Chrome reports a local file as "file://". The background took either as a site, so every such page was one site: a connection remembered for one covered them all, and the phishing check never matched. The background now parses the reported origin and, when it has no host, refuses the request with 4100 like one with no origin, before any method runs. tests/rpcOrigin.test.js sends both, and README's SiteApproval says what is refused. Model: opus-5-5
192 lines
5.9 KiB
JavaScript
192 lines
5.9 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).
|
|
//
|
|
// An origin with no host names no site, so its request is refused
|
|
// (https://git.eeqj.de/sneak/AutistMask/issues/527). The browser reports
|
|
// "null" for a page with an opaque origin, such as one its server sandboxes
|
|
// with a Content-Security-Policy, and Chrome reports "file://" for a local
|
|
// file.
|
|
//
|
|
// 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 whose origin has no host", () => {
|
|
test("from a page with an opaque origin is refused, whatever its 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.",
|
|
},
|
|
});
|
|
});
|
|
|
|
test("from a local file is refused", async () => {
|
|
const accounts = loadBackground();
|
|
|
|
const result = await accounts({
|
|
origin: "file://",
|
|
url: "file:///home/user/page.html",
|
|
tab: { url: "file:///home/user/page.html" },
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
error: {
|
|
code: 4100,
|
|
message:
|
|
"The wallet could not tell which site sent this request.",
|
|
},
|
|
});
|
|
});
|
|
});
|