From 9adae6c9c74d8777ca664fa784007bc6f49d8616 Mon Sep 17 00:00:00 2001 From: sneak Date: Wed, 14 Oct 2026 17:21:33 +0000 Subject: [PATCH] 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 --- README.md | 9 ++++++--- src/background/index.js | 7 ++++++- tests/rpcOrigin.test.js | 30 ++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d08f843..13dac89 100644 --- a/README.md +++ b/README.md @@ -2080,9 +2080,12 @@ view would leave a wallet one click from deletion. `wallet_requestPermissions` and is on neither the allowed nor the denied list. A site is its full origin, `scheme://host[:port]`, on both lists and for a connection allowed without "Remember": a choice for `https://dapp.example` - says nothing about `http://dapp.example` or another port of that host. The - background script prefers the toolbar popup (`action.openPopup()`) and falls - back to a separate popup window (`src/background/index.js`, + says nothing about `http://dapp.example` or another port of that host. A page + with an opaque origin, such as one its server sandboxes with a + Content-Security-Policy, is reported with the origin `null`, which is no site: + every request from it is refused with EIP-1193 code `4100` and opens no + prompt. The background script prefers the toolbar popup (`action.openPopup()`) + and falls back to a separate popup window (`src/background/index.js`, `requestApproval()`). Only one exists per site at a time: a further connection request from a site whose prompt is still unanswered is refused with EIP-1193 code `-32002` and opens no new prompt. If that prompt was in a toolbar popup diff --git a/src/background/index.js b/src/background/index.js index 2b55689..f0157d7 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1428,6 +1428,11 @@ runtime.onMessage.addListener((msg, sender, sendResponse) => { // the origin of sender.url is used: the frame that sent the message, // not the tab's page, which may be another site embedding that // frame. With neither, the request is refused. + // + // A page with an opaque origin, such as one its server sandboxes with + // a Content-Security-Policy, is reported with the origin "null". That + // is no site, so the request is refused too, and not credited to the + // site its URL names. let trustedOrigin = sender.origin; if (!trustedOrigin && sender.url) { try { @@ -1436,7 +1441,7 @@ runtime.onMessage.addListener((msg, sender, sendResponse) => { // an unparseable URL leaves the origin unknown } } - if (!trustedOrigin) { + if (!trustedOrigin || trustedOrigin === "null") { sendResponse({ error: { code: 4100, diff --git a/tests/rpcOrigin.test.js b/tests/rpcOrigin.test.js index 108b885..2d239d2 100644 --- a/tests/rpcOrigin.test.js +++ b/tests/rpcOrigin.test.js @@ -8,8 +8,14 @@ // 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. +// 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"); @@ -144,3 +150,23 @@ describe("a request the browser does not say the sender of", () => { }); }); }); + +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.", + }, + }); + }); +});