Compare commits

..
Author SHA1 Message Date
sneak 1606b5e568 chore: drop eth_chainId and net_version from PROXY_METHODS (closes #326)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run
handleRpc answers both methods itself before it reaches its proxy branch, so
the two list entries were never used and the list named two methods that are
never sent to the RPC endpoint. No other entry is answered earlier.

PROXY_METHODS is now exported from the background script so that
tests/proxyMethods.test.js can send every listed method from a page and fail
on any that does not reach the RPC endpoint.

Model: opus-5-5
2026-10-05 13:30:20 +00:00
6 changed files with 107 additions and 33 deletions
-3
View File
@@ -3,9 +3,6 @@ on: [push]
jobs: jobs:
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Bounds script/cibuild, a cold-cache build included, so a hang frees
# the shared runner. README.md "In CI" has the measured times.
timeout-minutes: 10
steps: steps:
# actions/checkout v4.2.2, 2026-02-22 # actions/checkout v4.2.2, 2026-02-22
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
-8
View File
@@ -35,10 +35,6 @@ on: [push]
jobs: jobs:
e2e-chrome: e2e-chrome:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Bounds the image build, a cold cache included, and both Chrome
# programs, so a hung browser frees the shared runner. README.md
# "In CI" has the measured times.
timeout-minutes: 20
steps: steps:
# actions/checkout v4.2.2, 2026-02-22 # actions/checkout v4.2.2, 2026-02-22
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -46,10 +42,6 @@ jobs:
e2e-firefox: e2e-firefox:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Bounds the image build, a cold cache included, and both Firefox
# programs, so a hung browser frees the shared runner. README.md
# "In CI" has the measured times.
timeout-minutes: 15
steps: steps:
# actions/checkout v4.2.2, 2026-02-22 # actions/checkout v4.2.2, 2026-02-22
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+6 -14
View File
@@ -643,20 +643,12 @@ Nothing in either job can pass vacuously. There is no `continue-on-error` and no
build fails, and when the browser fails to start; the Chrome harness aborts the build fails, and when the browser fails to start; the Chrome harness aborts the
suite outright if its network interception is not in effect. suite outright if its network interception is not in effect.
Measured on this repo's runner in the green runs of early October 2026, from a Measured on this repo's runner: `e2e-chrome` about 1m55s cold, almost all of it
warm docker cache to a cold one: `check` 49s to 3m37s, `e2e-chrome` 1m44s to the one-time pull of the pinned ~800MB Playwright layer, and well under a minute
4m48s, and `e2e-firefox` 31s to 4m07s. A cold cache adds three to four minutes once that layer is cached. `e2e-firefox` about 1m05s cold, and it caches its
to each job, spent rebuilding its image: reinstalling dependencies and, for Firefox and geckodriver downloads the same way. The `e2e-chrome` figures predate
`e2e-firefox`, installing Firefox, geckodriver and their system libraries. Those the two cases that wait for a receipt to end in error, which add about two
`e2e-chrome` runs predate the cases that wait in real time for a receipt to end minutes of real waiting.
in error. `make test-e2e` now takes 3m51s locally with its image cached, so a
cold `e2e-chrome` run comes to about seven minutes.
Every job has a `timeout-minutes` cap, so a hung build or browser ends the job
instead of holding the shared runner: `check` 10 minutes, `e2e-firefox` 15 and
`e2e-chrome` 20, each over two and a half times the job's slowest cold run. A
job that reaches its cap has hung; read it as a hang, not as a slow run to
retry.
### Element id guard (part of `make check`) ### Element id guard (part of `make check`)
+7 -5
View File
@@ -45,11 +45,13 @@ but the review is broader than any of them.
# Completed Steps # Completed Steps
- 2026-10-05: Every CI job has a `timeout-minutes` cap - 2026-10-05: `PROXY_METHODS` in `src/background/index.js` no longer lists
([#294](https://git.eeqj.de/sneak/AutistMask/issues/294)): `check` 10 minutes, `eth_chainId` and `net_version`
`e2e-firefox` 15 and `e2e-chrome` 20, each over two and a half times the job's ([#326](https://git.eeqj.de/sneak/AutistMask/issues/326)). `handleRpc` answers
slowest cold-cache run. A hung build or browser now ends its job instead of both itself before its proxy branch, so the list named two methods that are
holding the shared runner for hours. 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 - 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 ([#328](https://git.eeqj.de/sneak/AutistMask/issues/328)): `style-src` is
+5 -3
View File
@@ -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 = [ const PROXY_METHODS = [
"eth_blockNumber", "eth_blockNumber",
"eth_call", "eth_call",
"eth_chainId",
"eth_estimateGas", "eth_estimateGas",
"eth_gasPrice", "eth_gasPrice",
"eth_getBalance", "eth_getBalance",
@@ -759,7 +760,6 @@ const PROXY_METHODS = [
"eth_getTransactionReceipt", "eth_getTransactionReceipt",
"eth_maxPriorityFeePerGas", "eth_maxPriorityFeePerGas",
"eth_sendRawTransaction", "eth_sendRawTransaction",
"net_version",
"web3_clientVersion", "web3_clientVersion",
"eth_feeHistory", "eth_feeHistory",
"eth_getBlockTransactionCountByHash", "eth_getBlockTransactionCountByHash",
@@ -1802,3 +1802,5 @@ runtime.onMessage.addListener((msg, sender, sendResponse) => {
return false; return false;
} }
}); });
module.exports = { PROXY_METHODS };
+89
View File
@@ -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);
});