diff --git a/README.md b/README.md index b1ed58a..1a141ee 100644 --- a/README.md +++ b/README.md @@ -803,8 +803,7 @@ small while ensuring fresh coverage of new phishing domains. When a dApp on a blocklisted domain requests a wallet connection, transaction approval, or signature, the approval popup displays a prominent red warning banner alerting the user. The domain checker matches exact hostnames and all -parent domains (subdomain matching), with whitelist overrides for legitimate -sites that share a parent domain with a blocklisted entry. +parent domains (subdomain matching). #### Transaction Decoding diff --git a/src/shared/phishingDomains.js b/src/shared/phishingDomains.js index 6be9826..43f8588 100644 --- a/src/shared/phishingDomains.js +++ b/src/shared/phishingDomains.js @@ -21,17 +21,13 @@ const REFRESH_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours const DELTA_STORAGE_KEY = "phishing-delta"; const MAX_DELTA_BYTES = 256 * 1024; // 256 KiB -// Vendored sets — built once from the bundled JSON. +// Vendored set — built once from the bundled JSON. const vendoredBlacklist = new Set( (vendoredConfig.blacklist || []).map((d) => d.toLowerCase()), ); -const vendoredWhitelist = new Set( - (vendoredConfig.whitelist || []).map((d) => d.toLowerCase()), -); -// Delta sets — only entries from live list that are NOT in vendored. +// Delta set — only entries from live list that are NOT in vendored. let deltaBlacklist = new Set(); -let deltaWhitelist = new Set(); let lastFetchTime = 0; let fetchPromise = null; let refreshTimer = null; @@ -50,11 +46,6 @@ function loadDeltaFromStorage() { data.blacklist.map((d) => d.toLowerCase()), ); } - if (data.whitelist && Array.isArray(data.whitelist)) { - deltaWhitelist = new Set( - data.whitelist.map((d) => d.toLowerCase()), - ); - } } catch { // localStorage unavailable or corrupt — start empty } @@ -67,7 +58,6 @@ function saveDeltaToStorage() { try { const data = { blacklist: Array.from(deltaBlacklist), - whitelist: Array.from(deltaWhitelist), }; const json = JSON.stringify(data); if (json.length < MAX_DELTA_BYTES) { @@ -85,19 +75,15 @@ function saveDeltaToStorage() { * Load a pre-parsed config and compute the delta against the vendored list. * Used for both live fetches and testing. * - * @param {{ blacklist?: string[], whitelist?: string[] }} config + * @param {{ blacklist?: string[] }} config */ function loadConfig(config) { const liveBlacklist = (config.blacklist || []).map((d) => d.toLowerCase()); - const liveWhitelist = (config.whitelist || []).map((d) => d.toLowerCase()); // Delta = entries in the live list that are NOT in the vendored list deltaBlacklist = new Set( liveBlacklist.filter((d) => !vendoredBlacklist.has(d)), ); - deltaWhitelist = new Set( - liveWhitelist.filter((d) => !vendoredWhitelist.has(d)), - ); lastFetchTime = Date.now(); saveDeltaToStorage(); @@ -124,7 +110,6 @@ function hostnameVariants(hostname) { /** * Check if a hostname is on the phishing blocklist. * Checks delta first (fresh/recent scam sites), then vendored list. - * Whitelisted domains (delta + vendored) are never flagged. * * @param {string} hostname - The hostname to check. * @returns {boolean} @@ -133,11 +118,6 @@ function isPhishingDomain(hostname) { if (!hostname) return false; const variants = hostnameVariants(hostname); - // Whitelist takes priority — check delta whitelist first, then vendored - for (const v of variants) { - if (deltaWhitelist.has(v) || vendoredWhitelist.has(v)) return false; - } - // Check delta blacklist first (fresh/recent scam sites), then vendored for (const v of variants) { if (deltaBlacklist.has(v) || vendoredBlacklist.has(v)) return true; @@ -209,7 +189,6 @@ function getDeltaSize() { */ function _reset() { deltaBlacklist = new Set(); - deltaWhitelist = new Set(); lastFetchTime = 0; fetchPromise = null; if (refreshTimer) { @@ -232,7 +211,5 @@ module.exports = { _reset, // Exposed for testing only _getVendoredBlacklistSize: () => vendoredBlacklist.size, - _getVendoredWhitelistSize: () => vendoredWhitelist.size, _getDeltaBlacklist: () => deltaBlacklist, - _getDeltaWhitelist: () => deltaWhitelist, }; diff --git a/tests/phishingDomains.test.js b/tests/phishingDomains.test.js index 0b69bdd..762ea19 100644 --- a/tests/phishingDomains.test.js +++ b/tests/phishingDomains.test.js @@ -23,9 +23,7 @@ const { hostnameVariants, _reset, _getVendoredBlacklistSize, - _getVendoredWhitelistSize, _getDeltaBlacklist, - _getDeltaWhitelist, } = require("../src/shared/phishingDomains"); // Reset delta state before each test to avoid cross-test contamination. @@ -45,21 +43,12 @@ describe("phishingDomains", () => { expect(_getVendoredBlacklistSize()).toBeGreaterThan(100000); }); - test("vendored whitelist is loaded from bundled JSON", () => { - expect(_getVendoredWhitelistSize()).toBeGreaterThan(0); - }); - test("detects domains from vendored blacklist", () => { // These are well-known phishing domains in the vendored list expect(isPhishingDomain("hopprotocol.pro")).toBe(true); expect(isPhishingDomain("blast-pools.pages.dev")).toBe(true); }); - test("vendored whitelist overrides vendored blacklist", () => { - // opensea.pro is whitelisted in the vendored config - expect(isPhishingDomain("opensea.pro")).toBe(false); - }); - test("getBlocklistSize includes vendored entries", () => { expect(getBlocklistSize()).toBeGreaterThan(100000); }); @@ -99,7 +88,6 @@ describe("phishingDomains", () => { "brand-new-scam-site-xyz123.com", "hopprotocol.pro", // already in vendored ], - whitelist: [], }); // Only the new domain should be in the delta expect( @@ -109,30 +97,14 @@ describe("phishingDomains", () => { expect(getDeltaSize()).toBe(1); }); - test("delta whitelist entries are computed correctly", () => { - loadConfig({ - blacklist: [], - whitelist: [ - "new-safe-site-xyz789.com", - "opensea.pro", // already in vendored whitelist - ], - }); - expect(_getDeltaWhitelist().has("new-safe-site-xyz789.com")).toBe( - true, - ); - expect(_getDeltaWhitelist().has("opensea.pro")).toBe(false); - }); - test("re-loading config replaces previous delta", () => { loadConfig({ blacklist: ["first-scam-xyz.com"], - whitelist: [], }); expect(isPhishingDomain("first-scam-xyz.com")).toBe(true); loadConfig({ blacklist: ["second-scam-xyz.com"], - whitelist: [], }); expect(isPhishingDomain("first-scam-xyz.com")).toBe(false); expect(isPhishingDomain("second-scam-xyz.com")).toBe(true); @@ -142,7 +114,6 @@ describe("phishingDomains", () => { const baseSize = getBlocklistSize(); loadConfig({ blacklist: ["delta-only-scam-xyz.com"], - whitelist: [], }); expect(getBlocklistSize()).toBe(baseSize + 1); }); @@ -152,7 +123,6 @@ describe("phishingDomains", () => { test("detects domain from delta blacklist", () => { loadConfig({ blacklist: ["fresh-scam-xyz.com"], - whitelist: [], }); expect(isPhishingDomain("fresh-scam-xyz.com")).toBe(true); }); @@ -174,34 +144,13 @@ describe("phishingDomains", () => { test("detects subdomain of blacklisted domain (delta)", () => { loadConfig({ blacklist: ["delta-phish-xyz.com"], - whitelist: [], }); expect(isPhishingDomain("sub.delta-phish-xyz.com")).toBe(true); }); - test("delta whitelist overrides vendored blacklist", () => { - // hopprotocol.pro is in the vendored blacklist - expect(isPhishingDomain("hopprotocol.pro")).toBe(true); - loadConfig({ - blacklist: [], - whitelist: ["hopprotocol.pro"], - }); - // Now whitelisted via delta — should not be flagged - expect(isPhishingDomain("hopprotocol.pro")).toBe(false); - }); - - test("vendored whitelist overrides delta blacklist", () => { - loadConfig({ - blacklist: ["opensea.pro"], // opensea.pro is vendored-whitelisted - whitelist: [], - }); - expect(isPhishingDomain("opensea.pro")).toBe(false); - }); - test("case-insensitive matching", () => { loadConfig({ blacklist: ["Delta-Scam-XYZ.COM"], - whitelist: [], }); expect(isPhishingDomain("delta-scam-xyz.com")).toBe(true); expect(isPhishingDomain("DELTA-SCAM-XYZ.COM")).toBe(true); @@ -212,7 +161,7 @@ describe("phishingDomains", () => { expect(isPhishingDomain(null)).toBe(false); }); - test("handles config with no blacklist/whitelist keys", () => { + test("handles config with no blacklist key", () => { loadConfig({}); expect(getDeltaSize()).toBe(0); // Vendored list still works @@ -224,19 +173,16 @@ describe("phishingDomains", () => { test("saveDeltaToStorage persists delta under 256KiB", () => { loadConfig({ blacklist: ["persisted-scam-xyz.com"], - whitelist: ["persisted-safe-xyz.com"], }); const stored = localStorage.getItem("phishing-delta"); expect(stored).not.toBeNull(); const data = JSON.parse(stored); expect(data.blacklist).toContain("persisted-scam-xyz.com"); - expect(data.whitelist).toContain("persisted-safe-xyz.com"); }); test("delta is cleared on _reset", () => { loadConfig({ blacklist: ["temp-scam-xyz.com"], - whitelist: [], }); expect(getDeltaSize()).toBe(1); _reset(); @@ -251,7 +197,7 @@ describe("phishingDomains", () => { expect(isPhishingDomain("blast-pools.pages.dev")).toBe(true); }); - test("does not flag legitimate whitelisted domains", () => { + test("does not flag legitimate domains", () => { expect(isPhishingDomain("opensea.io")).toBe(false); expect(isPhishingDomain("etherscan.io")).toBe(false); });