Scope site connection permissions per address
Some checks failed
check / check (push) Has been cancelled

allowedSites and deniedSites are now objects keyed by address instead
of flat arrays, so approving a site for one address no longer grants
access for all addresses. Old flat-array data is discarded on load.
Settings view collects unique hostnames across all addresses and
deleting removes the site from every address.
This commit is contained in:
2026-02-26 03:54:52 +07:00
parent 21ccecab46
commit 980fdda694
3 changed files with 51 additions and 22 deletions

View File

@@ -19,7 +19,7 @@ const windowsApi =
typeof browser !== "undefined" ? browser.windows : chrome.windows; typeof browser !== "undefined" ? browser.windows : chrome.windows;
const tabsApi = typeof browser !== "undefined" ? browser.tabs : chrome.tabs; const tabsApi = typeof browser !== "undefined" ? browser.tabs : chrome.tabs;
// Connected sites (in-memory, non-persisted): { origin: true } // Connected sites (in-memory, non-persisted): { "origin:address": true }
const connectedSites = {}; const connectedSites = {};
// Pending approval requests: { id: { origin, hostname, resolve } } // Pending approval requests: { id: { origin, hostname, resolve } }
@@ -33,8 +33,8 @@ async function getState() {
wallets: [], wallets: [],
rpcUrl: DEFAULT_RPC_URL, rpcUrl: DEFAULT_RPC_URL,
activeAddress: null, activeAddress: null,
allowedSites: [], allowedSites: {},
deniedSites: [], deniedSites: {},
} }
); );
} }
@@ -114,9 +114,11 @@ async function handleConnectionRequest(origin) {
} }
const hostname = extractHostname(origin); const hostname = extractHostname(origin);
const allowed = s.allowedSites[activeAddress] || [];
const denied = s.deniedSites[activeAddress] || [];
// Check denied list // Check denied list
if (s.deniedSites.includes(hostname)) { if (denied.includes(hostname)) {
return { return {
error: { error: {
code: 4001, code: 4001,
@@ -126,7 +128,10 @@ async function handleConnectionRequest(origin) {
} }
// Check allowed list or in-memory connected // Check allowed list or in-memory connected
if (s.allowedSites.includes(hostname) || connectedSites[origin]) { if (
allowed.includes(hostname) ||
connectedSites[origin + ":" + activeAddress]
) {
return { result: [activeAddress] }; return { result: [activeAddress] };
} }
@@ -137,19 +142,25 @@ async function handleConnectionRequest(origin) {
if (decision.remember) { if (decision.remember) {
// Reload state to get latest, add to allowed, persist // Reload state to get latest, add to allowed, persist
await loadState(); await loadState();
if (!state.allowedSites.includes(hostname)) { if (!state.allowedSites[activeAddress]) {
state.allowedSites.push(hostname); state.allowedSites[activeAddress] = [];
}
if (!state.allowedSites[activeAddress].includes(hostname)) {
state.allowedSites[activeAddress].push(hostname);
} }
await saveState(); await saveState();
} else { } else {
connectedSites[origin] = true; connectedSites[origin + ":" + activeAddress] = true;
} }
return { result: [activeAddress] }; return { result: [activeAddress] };
} else { } else {
if (decision.remember) { if (decision.remember) {
await loadState(); await loadState();
if (!state.deniedSites.includes(hostname)) { if (!state.deniedSites[activeAddress]) {
state.deniedSites.push(hostname); state.deniedSites[activeAddress] = [];
}
if (!state.deniedSites[activeAddress].includes(hostname)) {
state.deniedSites[activeAddress].push(hostname);
} }
await saveState(); await saveState();
} }
@@ -198,7 +209,11 @@ async function handleRpc(method, params, origin) {
const activeAddress = await getActiveAddress(); const activeAddress = await getActiveAddress();
if (!activeAddress) return { result: [] }; if (!activeAddress) return { result: [] };
const hostname = extractHostname(origin); const hostname = extractHostname(origin);
if (s.allowedSites.includes(hostname) || connectedSites[origin]) { const allowed = s.allowedSites[activeAddress] || [];
if (
allowed.includes(hostname) ||
connectedSites[origin + ":" + activeAddress]
) {
return { result: [activeAddress] }; return { result: [activeAddress] };
} }
return { result: [] }; return { result: [] };
@@ -256,8 +271,10 @@ async function handleRpc(method, params, origin) {
const s = await getState(); const s = await getState();
const activeAddress = await getActiveAddress(); const activeAddress = await getActiveAddress();
const hostname = extractHostname(origin); const hostname = extractHostname(origin);
const allowed = s.allowedSites[activeAddress] || [];
const isConnected = const isConnected =
s.allowedSites.includes(hostname) || connectedSites[origin]; allowed.includes(hostname) ||
connectedSites[origin + ":" + activeAddress];
if (!isConnected || !activeAddress) { if (!isConnected || !activeAddress) {
return { result: [] }; return { result: [] };
} }

View File

@@ -6,25 +6,31 @@ const { log } = require("../../shared/log");
const runtime = const runtime =
typeof browser !== "undefined" ? browser.runtime : chrome.runtime; typeof browser !== "undefined" ? browser.runtime : chrome.runtime;
function renderSiteList(containerId, list, stateKey) { function renderSiteList(containerId, siteMap, stateKey) {
const container = $(containerId); const container = $(containerId);
if (list.length === 0) { const hostnames = [...new Set(Object.values(siteMap).flat())];
if (hostnames.length === 0) {
container.innerHTML = '<p class="text-xs text-muted">None</p>'; container.innerHTML = '<p class="text-xs text-muted">None</p>';
return; return;
} }
let html = ""; let html = "";
list.forEach((hostname, i) => { hostnames.forEach((hostname) => {
html += `<div class="flex justify-between items-center text-xs py-1 border-b border-border-light">`; html += `<div class="flex justify-between items-center text-xs py-1 border-b border-border-light">`;
html += `<span>${hostname}</span>`; html += `<span>${hostname}</span>`;
html += `<button class="btn-remove-site border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer" data-key="${stateKey}" data-index="${i}">[x]</button>`; html += `<button class="btn-remove-site border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer" data-key="${stateKey}" data-hostname="${hostname}">[x]</button>`;
html += `</div>`; html += `</div>`;
}); });
container.innerHTML = html; container.innerHTML = html;
container.querySelectorAll(".btn-remove-site").forEach((btn) => { container.querySelectorAll(".btn-remove-site").forEach((btn) => {
btn.addEventListener("click", async () => { btn.addEventListener("click", async () => {
const key = btn.dataset.key; const key = btn.dataset.key;
const idx = parseInt(btn.dataset.index, 10); const host = btn.dataset.hostname;
state[key].splice(idx, 1); for (const addr of Object.keys(state[key])) {
state[key][addr] = state[key][addr].filter((h) => h !== host);
if (state[key][addr].length === 0) {
delete state[key][addr];
}
}
await saveState(); await saveState();
runtime.sendMessage({ type: "AUTISTMASK_REMOVE_SITE" }); runtime.sendMessage({ type: "AUTISTMASK_REMOVE_SITE" });
renderSiteList(containerId, state[key], key); renderSiteList(containerId, state[key], key);

View File

@@ -15,8 +15,8 @@ const DEFAULT_STATE = {
blockscoutUrl: DEFAULT_BLOCKSCOUT_URL, blockscoutUrl: DEFAULT_BLOCKSCOUT_URL,
lastBalanceRefresh: 0, lastBalanceRefresh: 0,
activeAddress: null, activeAddress: null,
allowedSites: [], allowedSites: {},
deniedSites: [], deniedSites: {},
rememberSiteChoice: true, rememberSiteChoice: true,
}; };
@@ -54,8 +54,14 @@ async function loadState() {
saved.blockscoutUrl || DEFAULT_STATE.blockscoutUrl; saved.blockscoutUrl || DEFAULT_STATE.blockscoutUrl;
state.lastBalanceRefresh = saved.lastBalanceRefresh || 0; state.lastBalanceRefresh = saved.lastBalanceRefresh || 0;
state.activeAddress = saved.activeAddress || null; state.activeAddress = saved.activeAddress || null;
state.allowedSites = saved.allowedSites || []; state.allowedSites =
state.deniedSites = saved.deniedSites || []; saved.allowedSites && !Array.isArray(saved.allowedSites)
? saved.allowedSites
: {};
state.deniedSites =
saved.deniedSites && !Array.isArray(saved.deniedSites)
? saved.deniedSites
: {};
state.rememberSiteChoice = state.rememberSiteChoice =
saved.rememberSiteChoice !== undefined saved.rememberSiteChoice !== undefined
? saved.rememberSiteChoice ? saved.rememberSiteChoice