harden: key remembered site permissions by full origin (closes #402)
check / check (push) Failing after 2s
e2e / e2e-chrome (push) Failing after 3s
e2e / e2e-firefox (push) Failing after 2s

allowedSites and deniedSites held the bare hostname, so a grant to
https://dapp.example also authorised http://dapp.example and every port
on that host, and the connection, transaction and signature prompts
named only the hostname. Both lists now store and match the full origin
(scheme://host[:port]), the key the connections approved without
Remember already used. The prompts, the Settings site lists and
AUTISTMASK_REMOVE_SITE use the origin too. Entries saved by hostname
are not migrated (pre-1.0): they match no site.

Model: opus-5-5
This commit is contained in:
2026-10-04 15:43:58 +00:00
parent f24b5bca19
commit e557f21bb0
29 changed files with 470 additions and 231 deletions
+9 -10
View File
@@ -438,11 +438,10 @@ step(
await d.switchToWindow(popup);
await d.waitVisible("#view-approve-site");
const hostname = await d.text("#approve-hostname");
const origin = await d.text("#approve-origin");
assert(
hostname === "127.0.0.1",
"the site prompt names the wrong origin: " +
JSON.stringify(hostname),
origin === env.server.origin,
"the site prompt names the wrong origin: " + JSON.stringify(origin),
);
const shown = await d.text("#approve-address");
assert(
@@ -494,16 +493,16 @@ step(
const screen = await d.execute(
`return {
hostname: document.getElementById("approve-sign-hostname").textContent,
origin: document.getElementById("approve-sign-origin").textContent,
type: document.getElementById("approve-sign-type").textContent,
message: document.getElementById("approve-sign-message").textContent,
from: document.getElementById("approve-sign-from").textContent,
};`,
);
assert(
screen.hostname === "127.0.0.1",
screen.origin === env.server.origin,
"the sign prompt names the wrong origin: " +
JSON.stringify(screen.hostname),
JSON.stringify(screen.origin),
);
assert(
screen.type === "Personal message",
@@ -571,7 +570,7 @@ step(
const screen = await d.execute(
`return {
hostname: document.getElementById("approve-tx-hostname").textContent,
origin: document.getElementById("approve-tx-origin").textContent,
from: document.getElementById("approve-tx-from").textContent,
to: document.getElementById("approve-tx-to").textContent,
value: document.getElementById("approve-tx-value").textContent,
@@ -582,9 +581,9 @@ step(
};`,
);
assert(
screen.hostname === "127.0.0.1",
screen.origin === env.server.origin,
"the transaction prompt names the wrong origin: " +
JSON.stringify(screen.hostname),
JSON.stringify(screen.origin),
);
assert(
screen.from.toLowerCase().includes(env.address.toLowerCase()),
+16 -19
View File
@@ -35,6 +35,7 @@ const {
const {
DAPP_ORIGIN,
DAPP_URL,
PHISHING_DAPP_ORIGIN,
PHISHING_DAPP_URL,
FEE_ESTIMATE_WEI,
FEE_RESERVE_WEI,
@@ -2471,8 +2472,6 @@ test("a token whose symbol() returns markup renders as text (#307)", async (env)
// dApp, with real funds, against a real network. The RPC is stubbed
// throughout. That pass stays on the human list before 1.0.0.
const DAPP_HOSTNAME = new URL(DAPP_URL).hostname;
// The personal_sign payload. Sent as hex, which is what dApps send and what
// the popup requires — it calls getBytes() on the message — and displayed on
// the approval screen as the decoded text, which is what the user is agreeing
@@ -3016,11 +3015,10 @@ test("eth_requestAccounts rejected at the prompt returns a rejection (#183)", as
try {
await visible(popup, "#view-approve-site");
const hostname = await popup.locator("#approve-hostname").innerText();
const origin = await popup.locator("#approve-origin").innerText();
assert(
hostname === DAPP_HOSTNAME,
"the site prompt names the wrong origin: " +
JSON.stringify(hostname),
origin === DAPP_ORIGIN,
"the site prompt names the wrong origin: " + JSON.stringify(origin),
);
// The control for the phishing test below: this origin is not on the
@@ -3101,7 +3099,6 @@ test("a connect request from a blocklisted site is flagged (#219)", async (env)
// check and the real approval screen. Nothing about the list is stubbed —
// there is nothing left to stub, since the extension no longer fetches it.
const phishingDapp = await openDapp(env.ctx, PHISHING_DAPP_URL);
const hostname = new URL(PHISHING_DAPP_URL).hostname;
try {
await reserveApprovalTab(env);
await startRequest(
@@ -3114,15 +3111,15 @@ test("a connect request from a blocklisted site is flagged (#219)", async (env)
try {
await visible(popup, "#view-approve-site");
const shown = await popup.locator("#approve-hostname").innerText();
const shown = await popup.locator("#approve-origin").innerText();
assert(
shown === hostname,
shown === PHISHING_DAPP_ORIGIN,
"the site prompt names the wrong origin: " +
JSON.stringify(shown),
);
await visible(popup, "#approve-site-phishing-warning");
console.log("# phishing warning shown for " + hostname);
console.log("# phishing warning shown for " + PHISHING_DAPP_ORIGIN);
// Not remembered: a remembered decision for this origin would
// outlive the test.
@@ -3152,15 +3149,15 @@ test("personal_sign signs, and the signature recovers to the address (#183)", as
const boundary = await watchApprovalBoundary(popup, env);
const screen = await popup.evaluate(() => ({
hostname: document.getElementById("approve-sign-hostname").textContent,
origin: document.getElementById("approve-sign-origin").textContent,
type: document.getElementById("approve-sign-type").textContent,
message: document.getElementById("approve-sign-message").textContent,
from: document.getElementById("approve-sign-from").textContent,
}));
assert(
screen.hostname === DAPP_HOSTNAME,
screen.origin === DAPP_ORIGIN,
"the sign prompt names the wrong origin: " +
JSON.stringify(screen.hostname),
JSON.stringify(screen.origin),
);
assert(
screen.type === "Personal message",
@@ -3245,15 +3242,15 @@ test("eth_signTypedData_v4 signs, and the signature recovers (#183)", async (env
const boundary = await watchApprovalBoundary(popup, env);
const screen = await popup.evaluate(() => ({
hostname: document.getElementById("approve-sign-hostname").textContent,
origin: document.getElementById("approve-sign-origin").textContent,
type: document.getElementById("approve-sign-type").textContent,
message: document.getElementById("approve-sign-message").innerText,
from: document.getElementById("approve-sign-from").textContent,
}));
assert(
screen.hostname === DAPP_HOSTNAME,
screen.origin === DAPP_ORIGIN,
"the typed data prompt names the wrong origin: " +
JSON.stringify(screen.hostname),
JSON.stringify(screen.origin),
);
assert(
screen.type === "Typed data (EIP-712)",
@@ -3351,7 +3348,7 @@ test("eth_sendTransaction signs the approved transaction and broadcasts it (#183
const boundary = await watchApprovalBoundary(popup, env);
const screen = await popup.evaluate(() => ({
hostname: document.getElementById("approve-tx-hostname").textContent,
origin: document.getElementById("approve-tx-origin").textContent,
from: document.getElementById("approve-tx-from").textContent,
to: document.getElementById("approve-tx-to").textContent,
value: document.getElementById("approve-tx-value").textContent,
@@ -3361,9 +3358,9 @@ test("eth_sendTransaction signs the approved transaction and broadcasts it (#183
.classList.contains("hidden"),
}));
assert(
screen.hostname === DAPP_HOSTNAME,
screen.origin === DAPP_ORIGIN,
"the transaction prompt names the wrong origin: " +
JSON.stringify(screen.hostname),
JSON.stringify(screen.origin),
);
assert(
screen.from.toLowerCase().includes(env.expectedAddress.toLowerCase()),