Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30f57b9076 |
@@ -57,15 +57,6 @@ but the review is broader than any of them.
|
|||||||
change are not migrated (pre-1.0): they match no site, and Settings lists them
|
change are not migrated (pre-1.0): they match no site, and Settings lists them
|
||||||
until they are removed.
|
until they are removed.
|
||||||
|
|
||||||
- 2026-10-04: A page's request is credited only to the site the browser says
|
|
||||||
sent it ([#407](https://git.eeqj.de/sneak/AutistMask/issues/407)). Where the
|
|
||||||
browser does not give the sender's origin (Firefox before 126), the background
|
|
||||||
used the tab's page, so a frame from another site would have been treated as
|
|
||||||
the site embedding it, and with no tab it used an origin the page wrote into
|
|
||||||
the message. It now uses the URL of the frame that sent the message, and
|
|
||||||
refuses the request with code 4100 when the browser gives neither. The content
|
|
||||||
script no longer writes an origin into the message.
|
|
||||||
|
|
||||||
- 2026-10-04: `make test` takes 8-13s on the shared build host, down from
|
- 2026-10-04: `make test` takes 8-13s on the shared build host, down from
|
||||||
17-25s, measured in alternating runs before and after the change
|
17-25s, measured in alternating runs before and after the change
|
||||||
([#428](https://git.eeqj.de/sneak/AutistMask/issues/428)). Each popup boot in
|
([#428](https://git.eeqj.de/sneak/AutistMask/issues/428)). Each popup boot in
|
||||||
|
|||||||
+8
-19
@@ -1279,29 +1279,18 @@ if (windowsNs && windowsNs.onRemoved) {
|
|||||||
// Listen for messages from content scripts and popup
|
// Listen for messages from content scripts and popup
|
||||||
runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||||
if (msg.type === "AUTISTMASK_RPC") {
|
if (msg.type === "AUTISTMASK_RPC") {
|
||||||
// The origin is the one the browser reports for the sender, never one
|
// Derive origin from trusted sender info to prevent origin spoofing.
|
||||||
// the message carries. Firefox before 126 gives no sender.origin, so
|
// Chrome MV3 provides sender.origin; Firefox MV2 fallback uses sender.tab.url.
|
||||||
// the origin of sender.url is used: the frame that sent the message,
|
let trustedOrigin = msg.origin; // fallback only if sender info unavailable
|
||||||
// not the tab's page, which may be another site embedding that
|
if (sender.origin) {
|
||||||
// frame. With neither, the request is refused.
|
trustedOrigin = sender.origin;
|
||||||
let trustedOrigin = sender.origin;
|
} else if (sender.tab && sender.tab.url) {
|
||||||
if (!trustedOrigin && sender.url) {
|
|
||||||
try {
|
try {
|
||||||
trustedOrigin = new URL(sender.url).origin;
|
trustedOrigin = new URL(sender.tab.url).origin;
|
||||||
} catch {
|
} catch {
|
||||||
// an unparseable URL leaves the origin unknown
|
// keep fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!trustedOrigin) {
|
|
||||||
sendResponse({
|
|
||||||
error: {
|
|
||||||
code: 4100,
|
|
||||||
message:
|
|
||||||
"The wallet could not tell which site sent this request.",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
handleRpc(msg.method, msg.params, trustedOrigin)
|
handleRpc(msg.method, msg.params, trustedOrigin)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
sendResponse(response);
|
sendResponse(response);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ window.addEventListener("message", (event) => {
|
|||||||
id,
|
id,
|
||||||
method,
|
method,
|
||||||
params,
|
params,
|
||||||
|
origin: location.origin,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
// 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).
|
|
||||||
//
|
|
||||||
// Every 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.",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user