diff --git a/TODO.md b/TODO.md index 6feb978..5dd4b1e 100644 --- a/TODO.md +++ b/TODO.md @@ -45,6 +45,14 @@ but the review is broader than any of them. # Completed Steps +- 2026-10-05: `PROXY_METHODS` in `src/background/index.js` no longer lists + `eth_chainId` and `net_version` + ([#326](https://git.eeqj.de/sneak/AutistMask/issues/326)). `handleRpc` answers + both itself before its proxy branch, so the list named two methods that are + never sent to the RPC endpoint. No other entry is answered earlier. + `tests/proxyMethods.test.js` sends every listed method from a page and fails + on any that does not reach the RPC endpoint. + - 2026-10-05: The popup's Content Security Policy no longer allows inline style ([#328](https://git.eeqj.de/sneak/AutistMask/issues/328)): `style-src` is `'self'` in both manifests, pinned in `tests/manifest.test.js`. The 42 diff --git a/src/background/index.js b/src/background/index.js index 76c902d..bbeb4c9 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -741,11 +741,12 @@ async function handleConnectionRequest(origin) { } } -// Methods that are safe to proxy directly to the RPC node +// Methods that are safe to proxy directly to the RPC node. A method handleRpc +// answers before its proxy branch does not belong here: it would never reach +// the node. tests/proxyMethods.test.js sends every one of these. const PROXY_METHODS = [ "eth_blockNumber", "eth_call", - "eth_chainId", "eth_estimateGas", "eth_gasPrice", "eth_getBalance", @@ -759,7 +760,6 @@ const PROXY_METHODS = [ "eth_getTransactionReceipt", "eth_maxPriorityFeePerGas", "eth_sendRawTransaction", - "net_version", "web3_clientVersion", "eth_feeHistory", "eth_getBlockTransactionCountByHash", @@ -1802,3 +1802,5 @@ runtime.onMessage.addListener((msg, sender, sendResponse) => { return false; } }); + +module.exports = { PROXY_METHODS }; diff --git a/tests/proxyMethods.test.js b/tests/proxyMethods.test.js new file mode 100644 index 0000000..03abe51 --- /dev/null +++ b/tests/proxyMethods.test.js @@ -0,0 +1,89 @@ +// Every method in PROXY_METHODS is sent to the RPC node. +// +// handleRpc answers some methods itself before it reaches its proxy branch. A +// method listed in PROXY_METHODS but answered earlier never reaches the node, +// so the list would name a method that is not proxied +// (https://git.eeqj.de/sneak/AutistMask/issues/326). Each method is sent from +// a page here and must come back with what the node answered. + +const { makeStorageStub } = require("./support/storageStub"); + +async function settle() { + for (let i = 0; i < 50; i++) await Promise.resolve(); +} + +afterEach(() => { + delete global.chrome; + delete global.fetch; +}); + +test("every method in PROXY_METHODS reaches the RPC node", async () => { + 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(), + })); + + // The node answers each method with a value naming that method. + global.fetch = jest.fn(async (url, opts) => ({ + status: 200, + json: async () => ({ + jsonrpc: "2.0", + id: 1, + result: "node answered " + JSON.parse(opts.body).method, + }), + })); + + let messageListener = null; + global.chrome = { + storage: makeStorageStub({ + autistmask: { + networkId: "mainnet", + wallets: [], + allowedSites: {}, + deniedSites: {}, + }, + }), + runtime: { + getURL: (path) => "chrome-extension://autistmask/" + path, + onMessage: { + addListener: (fn) => { + messageListener = fn; + }, + }, + onConnect: { addListener: () => {} }, + lastError: null, + }, + windows: { onRemoved: { addListener: () => {} } }, + action: { setPopup: () => {} }, + }; + + const { PROXY_METHODS } = require("../src/background/index"); + + const answers = {}; + const expected = {}; + for (const method of PROXY_METHODS) { + messageListener( + { type: "AUTISTMASK_RPC", method, params: [] }, + { origin: "https://dapp.example" }, + (r) => { + answers[method] = r; + }, + ); + await settle(); + expected[method] = { result: "node answered " + method }; + } + + expect(PROXY_METHODS.length).toBeGreaterThan(0); + expect(answers).toEqual(expected); +});