diff --git a/src/data/phishing-domains.json b/src/data/phishing-domains.json index f1b2431..9a7b1f0 100644 --- a/src/data/phishing-domains.json +++ b/src/data/phishing-domains.json @@ -69,8 +69,6 @@ "web3modal.org" ], "blacklist": [ - "*.chrom-coinbse-extenson.pages.dev", - "*.coinbase-563513.com", "0-0.coinbase-com.info", "0-coinbase.com", "0-cz.com", diff --git a/src/shared/phishingDomains.js b/src/shared/phishingDomains.js index 4d0bfba..73ce79b 100644 --- a/src/shared/phishingDomains.js +++ b/src/shared/phishingDomains.js @@ -33,6 +33,19 @@ let lastFetchTime = 0; let fetchPromise = null; let persistedDeltaLoaded = false; +/** + * Normalize a domain entry: lowercase and strip wildcard prefix ("*."). + * Wildcard domains like "*.evil.com" become "evil.com" — our subdomain + * matching in hostnameVariants() already covers child domains. + * + * @param {string} domain + * @returns {string} + */ +function normalizeDomain(domain) { + const d = domain.toLowerCase(); + return d.startsWith("*.") ? d.slice(2) : d; +} + /** * Binary search on a sorted string array. * @@ -197,7 +210,7 @@ async function updatePhishingList() { // Compute blacklist delta: remote items not in vendored baseline const newDeltaBl = new Set(); for (const domain of config.blacklist || []) { - const d = domain.toLowerCase(); + const d = normalizeDomain(domain); if (!binarySearch(vendoredBlacklist, d)) { newDeltaBl.add(d); } @@ -206,7 +219,7 @@ async function updatePhishingList() { // Compute whitelist delta: remote items not in vendored whitelist const newDeltaWl = new Set(); for (const domain of config.whitelist || []) { - const d = domain.toLowerCase(); + const d = normalizeDomain(domain); if (!vendoredWhitelist.has(d)) { newDeltaWl.add(d); } @@ -236,12 +249,8 @@ async function updatePhishingList() { function loadConfig(config) { // For tests: treat the entire config as delta (overlaid on vendored). // Clear existing delta first. - deltaBlacklistSet = new Set( - (config.blacklist || []).map((d) => d.toLowerCase()), - ); - deltaWhitelistSet = new Set( - (config.whitelist || []).map((d) => d.toLowerCase()), - ); + deltaBlacklistSet = new Set((config.blacklist || []).map(normalizeDomain)); + deltaWhitelistSet = new Set((config.whitelist || []).map(normalizeDomain)); lastFetchTime = Date.now(); persistedDeltaLoaded = true; } @@ -283,5 +292,6 @@ module.exports = { getDeltaSize, hostnameVariants, binarySearch, + normalizeDomain, _reset, }; diff --git a/tests/phishingDomains.test.js b/tests/phishingDomains.test.js index e2eae83..545fbb8 100644 --- a/tests/phishingDomains.test.js +++ b/tests/phishingDomains.test.js @@ -5,6 +5,7 @@ const { getDeltaSize, hostnameVariants, binarySearch, + normalizeDomain, _reset, } = require("../src/shared/phishingDomains"); @@ -67,6 +68,35 @@ describe("phishingDomains", () => { }); }); + describe("normalizeDomain", () => { + test("strips *. wildcard prefix", () => { + expect(normalizeDomain("*.evil.com")).toBe("evil.com"); + expect(normalizeDomain("*.sub.evil.com")).toBe("sub.evil.com"); + }); + + test("lowercases domains", () => { + expect(normalizeDomain("Evil.COM")).toBe("evil.com"); + expect(normalizeDomain("*.Evil.COM")).toBe("evil.com"); + }); + + test("passes through normal domains unchanged", () => { + expect(normalizeDomain("example.com")).toBe("example.com"); + }); + }); + + describe("wildcard domain handling", () => { + test("wildcard blacklist entries match via loadConfig", () => { + loadConfig({ + blacklist: ["*.scam-site.com", "normal-scam.com"], + whitelist: [], + }); + // *.scam-site.com is normalized to scam-site.com + expect(isPhishingDomain("scam-site.com")).toBe(true); + expect(isPhishingDomain("sub.scam-site.com")).toBe(true); + expect(isPhishingDomain("normal-scam.com")).toBe(true); + }); + }); + describe("vendored baseline detection", () => { // These tests verify that the vendored phishing-domains.json // is loaded and searchable without any delta loaded.