fix: a request whose origin has no host 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", and Chrome reports a local file as "file://". The background took either as a site, so every such page was one site: a connection remembered for one covered them all, and the phishing check never matched. The background now parses the reported origin and, when it has no host, refuses the request with 4100 like one with no origin, before any method runs. tests/rpcOrigin.test.js sends both, and README's SiteApproval says what is refused. Model: opus-5-5
This commit is contained in:
@@ -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.
|
`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
|
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`
|
connection allowed without "Remember": a choice for `https://dapp.example`
|
||||||
says nothing about `http://dapp.example` or another port of that host. The
|
says nothing about `http://dapp.example` or another port of that host. A
|
||||||
background script prefers the toolbar popup (`action.openPopup()`) and falls
|
request whose origin has no host names no site, so it is refused with EIP-1193
|
||||||
back to a separate popup window (`src/background/index.js`,
|
code `4100` and opens no prompt. Two such origins are `null`, which the
|
||||||
`requestApproval()`). Only one exists per site at a time: a further connection
|
browser reports for a page with an opaque origin, such as one its server
|
||||||
request from a site whose prompt is still unanswered is refused with EIP-1193
|
sandboxes with a Content-Security-Policy, and `file://`, which Chrome reports
|
||||||
code `-32002` and opens no new prompt. If that prompt was in a toolbar popup
|
for a local file. The background script prefers the toolbar popup
|
||||||
that closed before it connected, and the toolbar popup has since been set to
|
(`action.openPopup()`) and falls back to a separate popup window
|
||||||
open something else, the refused request shows that prompt again.
|
(`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**:
|
- **Elements**:
|
||||||
- "Connection Request" heading
|
- "Connection Request" heading
|
||||||
- Phishing warning banner (shown when the hostname is on the phishing
|
- Phishing warning banner (shown when the hostname is on the phishing
|
||||||
|
|||||||
+15
-2
@@ -1457,7 +1457,14 @@ runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|||||||
// the message carries. Firefox before 126 gives no sender.origin, so
|
// the message carries. Firefox before 126 gives no sender.origin, so
|
||||||
// the origin of sender.url is used: the frame that sent the message,
|
// 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
|
// 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;
|
let trustedOrigin = sender.origin;
|
||||||
if (!trustedOrigin && sender.url) {
|
if (!trustedOrigin && sender.url) {
|
||||||
try {
|
try {
|
||||||
@@ -1466,7 +1473,13 @@ runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|||||||
// an unparseable URL leaves the origin unknown
|
// 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({
|
sendResponse({
|
||||||
error: {
|
error: {
|
||||||
code: 4100,
|
code: 4100,
|
||||||
|
|||||||
+47
-2
@@ -8,8 +8,15 @@
|
|||||||
// and a request the browser said nothing about was credited to whatever the
|
// and a request the browser said nothing about was credited to whatever the
|
||||||
// page wrote (https://git.eeqj.de/sneak/AutistMask/issues/407).
|
// page wrote (https://git.eeqj.de/sneak/AutistMask/issues/407).
|
||||||
//
|
//
|
||||||
// Every sender here lacks sender.origin, as on old Firefox. The connection
|
// An origin with no host names no site, so its request is refused
|
||||||
// check on eth_accounts is what shows which site a request was credited to.
|
// (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");
|
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.",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user