diff --git a/README.md b/README.md index 35e0a95..975c2ce 100644 --- a/README.md +++ b/README.md @@ -2129,14 +2129,19 @@ 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`, - `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 - that closed before it connected, and the toolbar popup has since been set to - open something else, the refused request shows that prompt again. + says nothing about `http://dapp.example` or another port of that host. A + request whose origin has no host names no site, so it is refused with EIP-1193 + code `4100` and opens no prompt. Two such origins are `null`, which the + browser reports for a page with an opaque origin, such as one its server + sandboxes with a Content-Security-Policy, and `file://`, which Chrome reports + for a local file. 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 that closed before it connected, and the + toolbar popup has since been set to open something else, the refused request + shows that prompt again. - **Elements**: - "Connection Request" heading - Phishing warning banner (shown when the hostname is on the phishing diff --git a/src/background/index.js b/src/background/index.js index a2bc512..8b3fe77 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1457,7 +1457,14 @@ runtime.onMessage.addListener((msg, sender, sendResponse) => { // the message carries. Firefox before 126 gives no sender.origin, so // 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. + // frame. + // + // A site is scheme://host[:port], so a request is refused when its + // origin has no host, as when it has no origin at all, rather than + // credited to the site its URL names. 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. let trustedOrigin = sender.origin; if (!trustedOrigin && sender.url) { try { @@ -1466,7 +1473,13 @@ runtime.onMessage.addListener((msg, sender, sendResponse) => { // an unparseable URL leaves the origin unknown } } - if (!trustedOrigin) { + let host = ""; + try { + host = new URL(trustedOrigin).host; + } catch { + // no origin, or "null", is not a URL and so has no host + } + if (!host) { sendResponse({ error: { code: 4100, diff --git a/tests/rpcOrigin.test.js b/tests/rpcOrigin.test.js index 108b885..e72c700 100644 --- a/tests/rpcOrigin.test.js +++ b/tests/rpcOrigin.test.js @@ -8,8 +8,15 @@ // 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. +// 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"); @@ -144,3 +151,41 @@ describe("a request the browser does not say the sender of", () => { }); }); }); + +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.", + }, + }); + }); +});