fix: harden extension security and plugin reliability

Address audit findings across background handlers, openers,
plugins, and UI: URL allowlists, XSS reductions, popup lifecycle
fixes, plugin dispose/cleanup, cloud sync hardening, global search
mathjs sandbox, and settings storage fixes.
This commit is contained in:
2026-06-17 10:50:26 +09:30
parent 0e696e0175
commit 8a5424c5a4
70 changed files with 1229 additions and 430 deletions
+50
View File
@@ -0,0 +1,50 @@
const ALLOWED_HOST_SUFFIXES = [
"betterseqta.org",
"accounts.betterseqta.org",
"raw.githubusercontent.com",
"github.com",
] as const;
function isPrivateOrLocalHost(hostname: string): boolean {
const host = hostname.toLowerCase().replace(/^\[|\]$/g, "");
if (host === "localhost" || host.endsWith(".localhost")) return true;
if (host === "127.0.0.1" || host.startsWith("127.")) return true;
if (host === "::1" || host === "0.0.0.0") return true;
const ipv4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
if (ipv4) {
const [a, b] = [Number(ipv4[1]), Number(ipv4[2])];
if (a === 10) return true;
if (a === 172 && b >= 16 && b <= 31) return true;
if (a === 192 && b === 168) return true;
if (a === 169 && b === 254) return true;
if (a === 127 || a === 0) return true;
}
if (host.includes(":")) {
const h = host.split("%")[0];
if (h.startsWith("fe80") || h.startsWith("fc") || h.startsWith("fd")) return true;
}
return false;
}
function isAllowedHost(hostname: string): boolean {
const host = hostname.toLowerCase();
return ALLOWED_HOST_SUFFIXES.some(
(suffix) => host === suffix || host.endsWith(`.${suffix}`),
);
}
/** HTTPS-only fetch allowlist for background `fetchFromUrl`. */
export function isAllowedFetchUrl(urlString: string): boolean {
let parsed: URL;
try {
parsed = new URL(urlString);
} catch {
return false;
}
if (parsed.protocol !== "https:") return false;
if (isPrivateOrLocalHost(parsed.hostname)) return false;
return isAllowedHost(parsed.hostname);
}