// Provide a localStorage mock for Node.js test environment. // Must be set before requiring the module since it calls loadDeltaFromStorage() // at module load time. const localStorageStore = {}; global.localStorage = { getItem: (key) => Object.prototype.hasOwnProperty.call(localStorageStore, key) ? localStorageStore[key] : null, setItem: (key, value) => { localStorageStore[key] = String(value); }, removeItem: (key) => { delete localStorageStore[key]; }, }; const { isPhishingDomain, loadConfig, getBlocklistSize, getDeltaSize, hostnameVariants, _reset, _getVendoredBlacklistSize, _getDeltaBlacklist, } = require("../src/shared/phishingDomains"); // Reset delta state before each test to avoid cross-test contamination. // Note: vendored sets are immutable and always present. beforeEach(() => { _reset(); // Clear localStorage mock between tests for (const key of Object.keys(localStorageStore)) { delete localStorageStore[key]; } }); describe("phishingDomains", () => { describe("vendored blocklist", () => { test("vendored blacklist is loaded from bundled JSON", () => { // The vendored blocklist should have a large number of entries expect(_getVendoredBlacklistSize()).toBeGreaterThan(100000); }); 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("getBlocklistSize includes vendored entries", () => { expect(getBlocklistSize()).toBeGreaterThan(100000); }); }); describe("hostnameVariants", () => { test("returns exact hostname plus parent domains", () => { const variants = hostnameVariants("sub.evil.com"); expect(variants).toEqual(["sub.evil.com", "evil.com"]); }); test("returns just the hostname for a bare domain", () => { const variants = hostnameVariants("example.com"); expect(variants).toEqual(["example.com"]); }); test("handles deep subdomain chains", () => { const variants = hostnameVariants("a.b.c.d.com"); expect(variants).toEqual([ "a.b.c.d.com", "b.c.d.com", "c.d.com", "d.com", ]); }); test("lowercases hostnames", () => { const variants = hostnameVariants("Evil.COM"); expect(variants).toEqual(["evil.com"]); }); }); describe("delta computation via loadConfig", () => { test("loadConfig computes delta of new entries not in vendored list", () => { loadConfig({ blacklist: [ "brand-new-scam-site-xyz123.com", "hopprotocol.pro", // already in vendored ], }); // Only the new domain should be in the delta expect( _getDeltaBlacklist().has("brand-new-scam-site-xyz123.com"), ).toBe(true); expect(_getDeltaBlacklist().has("hopprotocol.pro")).toBe(false); expect(getDeltaSize()).toBe(1); }); test("re-loading config replaces previous delta", () => { loadConfig({ blacklist: ["first-scam-xyz.com"], }); expect(isPhishingDomain("first-scam-xyz.com")).toBe(true); loadConfig({ blacklist: ["second-scam-xyz.com"], }); expect(isPhishingDomain("first-scam-xyz.com")).toBe(false); expect(isPhishingDomain("second-scam-xyz.com")).toBe(true); }); test("getBlocklistSize includes both vendored and delta", () => { const baseSize = getBlocklistSize(); loadConfig({ blacklist: ["delta-only-scam-xyz.com"], }); expect(getBlocklistSize()).toBe(baseSize + 1); }); }); describe("isPhishingDomain with delta + vendored", () => { test("detects domain from delta blacklist", () => { loadConfig({ blacklist: ["fresh-scam-xyz.com"], }); expect(isPhishingDomain("fresh-scam-xyz.com")).toBe(true); }); test("detects domain from vendored blacklist", () => { // No delta loaded — vendored still works expect(isPhishingDomain("hopprotocol.pro")).toBe(true); }); test("returns false for clean domains", () => { expect(isPhishingDomain("etherscan.io")).toBe(false); expect(isPhishingDomain("example.com")).toBe(false); }); test("detects subdomain of blacklisted domain (vendored)", () => { expect(isPhishingDomain("app.hopprotocol.pro")).toBe(true); }); test("detects subdomain of blacklisted domain (delta)", () => { loadConfig({ blacklist: ["delta-phish-xyz.com"], }); expect(isPhishingDomain("sub.delta-phish-xyz.com")).toBe(true); }); test("case-insensitive matching", () => { loadConfig({ blacklist: ["Delta-Scam-XYZ.COM"], }); expect(isPhishingDomain("delta-scam-xyz.com")).toBe(true); expect(isPhishingDomain("DELTA-SCAM-XYZ.COM")).toBe(true); }); test("returns false for empty/null hostname", () => { expect(isPhishingDomain("")).toBe(false); expect(isPhishingDomain(null)).toBe(false); }); test("handles config with no blacklist key", () => { loadConfig({}); expect(getDeltaSize()).toBe(0); // Vendored list still works expect(isPhishingDomain("hopprotocol.pro")).toBe(true); }); }); describe("localStorage persistence", () => { test("saveDeltaToStorage persists delta under 256KiB", () => { loadConfig({ blacklist: ["persisted-scam-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"); }); test("delta is cleared on _reset", () => { loadConfig({ blacklist: ["temp-scam-xyz.com"], }); expect(getDeltaSize()).toBe(1); _reset(); expect(getDeltaSize()).toBe(0); }); }); describe("real-world blocklist patterns", () => { test("detects known phishing domains from vendored list", () => { expect(isPhishingDomain("uniswap-trade.web.app")).toBe(true); expect(isPhishingDomain("hopprotocol.pro")).toBe(true); expect(isPhishingDomain("blast-pools.pages.dev")).toBe(true); }); test("does not flag legitimate domains", () => { expect(isPhishingDomain("opensea.io")).toBe(false); expect(isPhishingDomain("etherscan.io")).toBe(false); }); }); });