From ded099369ca3a8e7d2440ca7a2b67c5cbd330dd1 Mon Sep 17 00:00:00 2001 From: brenopolanski Date: Thu, 30 Oct 2025 15:16:47 -0300 Subject: [PATCH 1/6] feat: re-add phishing detection --- src/pages/popup/Popup.tsx | 71 +++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/src/pages/popup/Popup.tsx b/src/pages/popup/Popup.tsx index 4054bde..e69e26c 100644 --- a/src/pages/popup/Popup.tsx +++ b/src/pages/popup/Popup.tsx @@ -36,7 +36,7 @@ const Popup = () => { setSearchResults([]); return; } - + const results = protocolDirectory .filter(item => { const nameMatch = item.name.toLowerCase().includes(searchTerm.toLowerCase()); @@ -44,7 +44,7 @@ const Popup = () => { return nameMatch || urlMatch; }) .slice(0, 5); // Limit to 5 results - + setSearchResults(results); }, 200), [searchTerm] @@ -54,6 +54,7 @@ const Popup = () => { const [tagsInjector, setTagsInjector] = useBrowserStorage("local", "settings:tagsInjector", true); const [explorerSpamHide, setExplorerSpamHide] = useBrowserStorage("local", "settings:explorerSpamHide", false); + const [phishingDetector, setPhishingDetector] = useBrowserStorage("local", "settings:phishingDetector", true); const [phishingHandleDetector, setPhishingHandleDetector] = useBrowserStorage( "local", "settings:phishingHandleDetector", @@ -74,53 +75,53 @@ const Popup = () => { DefiLlama - + {/* Quick Links */} - DefiLlama DeFiLlama - LlamaSwap LlamaSwap - LlamaPay LlamaPay - LlamaFeed LlamaFeed @@ -151,7 +152,7 @@ const Popup = () => { bg={useColorModeValue("white", "gray.700")} /> - + {/* Search Results */} {searchResults.length > 0 && ( { - + Twitter @@ -267,7 +268,7 @@ const Popup = () => { /> - + Explorer @@ -301,6 +302,24 @@ const Popup = () => { }} /> + + + Phishing Protection + + + + Enable phishing detection + { + setPhishingDetector(e.target.checked); + if (!e.target.checked) { + Browser.action.setIcon({ path: cuteStatic }); + } + }} + /> + From d805a9f872dad72c0370f390c90e28a29c9cfed7 Mon Sep 17 00:00:00 2001 From: brenopolanski Date: Thu, 30 Oct 2025 15:25:38 -0300 Subject: [PATCH 2/6] feat: check if phishing detection is enabled --- src/pages/background/index.ts | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/pages/background/index.ts b/src/pages/background/index.ts index 52f1415..d40d147 100644 --- a/src/pages/background/index.ts +++ b/src/pages/background/index.ts @@ -84,15 +84,29 @@ async function handleDomainCheck(trigger: string, tab?: Browser.Tabs.Tab) { } async function handlePhishingCheck(trigger: string, tab?: Browser.Tabs.Tab) { + // Check if phishing detection is enabled + const phishingDetector = await getStorage("local", "settings:phishingDetector", true); + if (!phishingDetector) { + // Reset to default icon when phishing detection is disabled + if (!tab) { + tab = await getCurrentTab(); + } + if (tab?.active) { + Browser.action.setIcon({ path: cute }); + Browser.action.setTitle({ title: "DefiLlama" }); + } + return; + } + const domainResult = await handleDomainCheck(trigger, tab); const { isBlocked, isTrusted, reason } = domainResult; tab = domainResult.tab; - + if (isBlocked) { // Always send warning message for blocked sites, regardless of active status if (tab?.id) { try { - await Browser.tabs.sendMessage(tab.id, { + await Browser.tabs.sendMessage(tab.id, { type: "DOMAIN_STATUS", status: "blocked", reason: reason @@ -101,7 +115,7 @@ async function handlePhishingCheck(trigger: string, tab?: Browser.Tabs.Tab) { // Tab might be closed or content script not ready - fail silently } } - + // Only update icon if this is the active tab if (tab?.active) { Browser.action.setIcon({ path: maxPain }); @@ -137,7 +151,7 @@ Browser.tabs.onUpdated.addListener(async (tabId, onUpdatedInfo, tab) => { if (onUpdatedInfo.url || onUpdatedInfo.status === "complete") { if (!tab?.active) return; - + const key = `${tab.id}-${tab.url}`; if (lastCheckKey === key) { return; @@ -158,7 +172,7 @@ Browser.tabs.onActivated.addListener(async (onActivatedInfo) => { } catch { // Content script might not be ready } - + const tab = await Browser.tabs.get(onActivatedInfo.tabId); await handlePhishingCheck('tabActivated', tab); } catch (error) { From 4c0245fc6f00a65d8476aad221e17cf7a5d4ec45 Mon Sep 17 00:00:00 2001 From: brenopolanski Date: Thu, 30 Oct 2025 15:32:29 -0300 Subject: [PATCH 3/6] chore: update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5743e9d..85202ec 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "defillama-extension", "private": true, - "version": "0.0.8.7", + "version": "0.0.8.8", "type": "module", "description": "DefiLlama Extension", "displayName": "DefiLlama", From 759df7ca868e5d5818fc26159af97478568decf2 Mon Sep 17 00:00:00 2001 From: brenopolanski Date: Thu, 30 Oct 2025 17:12:47 -0300 Subject: [PATCH 4/6] feat: add fuzzy matching option --- src/pages/popup/Popup.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/pages/popup/Popup.tsx b/src/pages/popup/Popup.tsx index e69e26c..d3cfb02 100644 --- a/src/pages/popup/Popup.tsx +++ b/src/pages/popup/Popup.tsx @@ -55,6 +55,7 @@ const Popup = () => { const [explorerSpamHide, setExplorerSpamHide] = useBrowserStorage("local", "settings:explorerSpamHide", false); const [phishingDetector, setPhishingDetector] = useBrowserStorage("local", "settings:phishingDetector", true); + const [phishingFuzzyMatch, setPhishingFuzzyMatch] = useBrowserStorage("local", "settings:phishingFuzzyMatch", false); const [phishingHandleDetector, setPhishingHandleDetector] = useBrowserStorage( "local", "settings:phishingHandleDetector", @@ -320,6 +321,16 @@ const Popup = () => { }} /> + + Enable fuzzy matching + { + setPhishingFuzzyMatch(e.target.checked); + }} + /> + From c03fc872f33b8ebb16a268a71eb42a04ad152fe3 Mon Sep 17 00:00:00 2001 From: brenopolanski Date: Thu, 30 Oct 2025 17:24:13 -0300 Subject: [PATCH 5/6] feat: only check fuzzy matching if enabled --- src/pages/background/index.ts | 18 +++++++++++--- src/pages/libs/phishing-detector.ts | 38 +++++++++++++++++------------ 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/pages/background/index.ts b/src/pages/background/index.ts index d40d147..ef91bba 100644 --- a/src/pages/background/index.ts +++ b/src/pages/background/index.ts @@ -67,9 +67,22 @@ async function handleDomainCheck(trigger: string, tab?: Browser.Tabs.Tab) { const parsed = psl.parse(hostname); const domain = (parsed && 'domain' in parsed && parsed.domain) || hostname.replace("www.", ""); - const res = await checkDomain(domain); + // Get fuzzy matching setting + const phishingFuzzyMatch = await getStorage("local", "settings:phishingFuzzyMatch", false); + const res = await checkDomain(domain, phishingFuzzyMatch); + if (res.result) { - const reason = res.type === "blocked" ? "Website is blacklisted" : "Suspicious website detected"; + let reason: string; + switch (res.type) { + case "blocked": + reason = "Website is blacklisted"; + break; + case "fuzzy": + reason = `Website impersonating ${res.extra}`; + break; + default: + reason = "Suspicious website detected"; + } return { isBlocked: true, isTrusted: false, reason, tab }; } @@ -77,7 +90,6 @@ async function handleDomainCheck(trigger: string, tab?: Browser.Tabs.Tab) { const reason = isTrusted ? "Website is whitelisted" : "Unknown website"; return { isBlocked: false, isTrusted, reason, tab }; - } catch (error) { return { isBlocked: false, isTrusted: false, reason: "Error checking domain", tab }; } diff --git a/src/pages/libs/phishing-detector.ts b/src/pages/libs/phishing-detector.ts index e07d6b6..628e020 100644 --- a/src/pages/libs/phishing-detector.ts +++ b/src/pages/libs/phishing-detector.ts @@ -23,43 +23,49 @@ export function clearDomainCheckCache() { const defaultCheckResponse = { result: false, type: "unknown" } as CheckDomainResult; -export async function checkDomain(domain: string): Promise { +export async function checkDomain(domain: string, enableFuzzyMatch = true): Promise { if (!domain) return defaultCheckResponse; clearDomainCheckCache(); - if (!domainCheckCache.has(domain)) { - domainCheckCache.set(domain, _checkDomain(domain)); + const cacheKey = `${domain}-${enableFuzzyMatch}`; + if (!domainCheckCache.has(cacheKey)) { + domainCheckCache.set(cacheKey, _checkDomain(domain, enableFuzzyMatch)); } return domainCheckCache.get(domain) || defaultCheckResponse; } -function _checkDomain(domain: string): CheckDomainResult { +function _checkDomain(domain: string, enableFuzzyMatch: boolean): CheckDomainResult { const parsed = psl.parse(domain); if (!parsed || !('domain' in parsed) || !parsed.domain) { const fallbackDomain = domain.split(".").slice(-2).join("."); - return checkDomainInLists(domain, fallbackDomain); + return checkDomainInLists(domain, fallbackDomain, enableFuzzyMatch); } const rootDomain = parsed.domain; - return checkDomainInLists(domain, rootDomain); + return checkDomainInLists(domain, rootDomain, enableFuzzyMatch); } -function checkDomainInLists(fullDomain: string, rootDomain: string): CheckDomainResult { +function checkDomainInLists(fullDomain: string, rootDomain: string, enableFuzzyMatch: boolean): CheckDomainResult { const isAllowed = allowedDomainsDb.data.has(fullDomain) || allowedDomainsDb.data.has(rootDomain); if (isAllowed) return { result: false, type: "allowed" }; const isBlocked = blockedDomainsDb.data.has(fullDomain) || blockedDomainsDb.data.has(rootDomain); if (isBlocked) return { result: true, type: "blocked" }; - let fuzzyResult: CheckDomainResult | undefined; - for (const fuzzyDomain of fuzzyDomainsDb.data) { - const fullDistance = levenshtein.get(fuzzyDomain, fullDomain); - const rootDistance = levenshtein.get(fuzzyDomain, rootDomain); - const minDistance = Math.min(fullDistance, rootDistance); - if (minDistance <= DEFAULT_LEVENSHTEIN_TOLERANCE) { - fuzzyResult = { result: false, type: "unknown", extra: fuzzyDomain }; - break; // Found a match, exit early + // Only check fuzzy matching if enabled + if (enableFuzzyMatch) { + let fuzzyResult: CheckDomainResult | undefined; + for (const fuzzyDomain of fuzzyDomainsDb.data) { + const fullDistance = levenshtein.get(fuzzyDomain, fullDomain); + const rootDistance = levenshtein.get(fuzzyDomain, rootDomain); + const minDistance = Math.min(fullDistance, rootDistance); + if (minDistance <= DEFAULT_LEVENSHTEIN_TOLERANCE) { + fuzzyResult = { result: false, type: "unknown", extra: fuzzyDomain }; + break; // Found a match, exit early + } } + if (fuzzyResult) return fuzzyResult; } - return fuzzyResult ?? defaultCheckResponse; + + return defaultCheckResponse; } From 0e63779d6a1fc714cde177bdf0ae012d09bd9c44 Mon Sep 17 00:00:00 2001 From: brenopolanski Date: Thu, 30 Oct 2025 21:58:14 -0300 Subject: [PATCH 6/6] refactor: some tweaks --- src/pages/background/index.ts | 19 +++++++------------ src/pages/libs/phishing-detector.ts | 9 ++++----- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/pages/background/index.ts b/src/pages/background/index.ts index ef91bba..2927cd6 100644 --- a/src/pages/background/index.ts +++ b/src/pages/background/index.ts @@ -24,8 +24,7 @@ initBackground(); Browser.runtime.onMessage.addListener((message, sender) => { try { if (message?.type === "CHECK_CURRENT_DOMAIN" && sender?.tab) { - handlePhishingCheck('contentScriptRequest', sender.tab).catch(() => { - }); + handlePhishingCheck("contentScriptRequest", sender.tab).catch(() => {}); } } catch (error) { } @@ -65,7 +64,7 @@ async function handleDomainCheck(trigger: string, tab?: Browser.Tabs.Tab) { } const parsed = psl.parse(hostname); - const domain = (parsed && 'domain' in parsed && parsed.domain) || hostname.replace("www.", ""); + const domain = (parsed && "domain" in parsed && parsed.domain) || hostname.replace("www.", ""); // Get fuzzy matching setting const phishingFuzzyMatch = await getStorage("local", "settings:phishingFuzzyMatch", false); @@ -121,7 +120,7 @@ async function handlePhishingCheck(trigger: string, tab?: Browser.Tabs.Tab) { await Browser.tabs.sendMessage(tab.id, { type: "DOMAIN_STATUS", status: "blocked", - reason: reason + reason }); } catch (error) { // Tab might be closed or content script not ready - fail silently @@ -150,7 +149,6 @@ async function handlePhishingCheck(trigger: string, tab?: Browser.Tabs.Tab) { let lastCheckKey = ""; - Browser.tabs.onUpdated.addListener(async (tabId, onUpdatedInfo, tab) => { try { if (onUpdatedInfo.status === "complete" && tab.active) { @@ -169,14 +167,13 @@ Browser.tabs.onUpdated.addListener(async (tabId, onUpdatedInfo, tab) => { return; } lastCheckKey = key; - await handlePhishingCheck('tabUpdate', tab); + await handlePhishingCheck("tabUpdate", tab); } } catch (error) { // Silently handle any tab update errors } }); - Browser.tabs.onActivated.addListener(async (onActivatedInfo) => { try { try { @@ -186,13 +183,12 @@ Browser.tabs.onActivated.addListener(async (onActivatedInfo) => { } const tab = await Browser.tabs.get(onActivatedInfo.tabId); - await handlePhishingCheck('tabActivated', tab); + await handlePhishingCheck("tabActivated", tab); } catch (error) { // Silently handle tab activation errors } }); - Browser.windows.onFocusChanged.addListener(async (windowId) => { try { if (windowId === Browser.windows.WINDOW_ID_NONE) return; @@ -203,18 +199,17 @@ Browser.windows.onFocusChanged.addListener(async (windowId) => { } catch { // Content script might not be ready } - await handlePhishingCheck('windowFocused', tab); + await handlePhishingCheck("windowFocused", tab); } } catch (error) { // Silently handle window focus errors } }); - Browser.tabs.onCreated.addListener(async (tab) => { try { if (tab.url && tab.active) { - await handlePhishingCheck('tabCreated', tab); + await handlePhishingCheck("tabCreated", tab); } } catch (error) { // Silently handle tab creation errors diff --git a/src/pages/libs/phishing-detector.ts b/src/pages/libs/phishing-detector.ts index 628e020..8d73f93 100644 --- a/src/pages/libs/phishing-detector.ts +++ b/src/pages/libs/phishing-detector.ts @@ -23,7 +23,7 @@ export function clearDomainCheckCache() { const defaultCheckResponse = { result: false, type: "unknown" } as CheckDomainResult; -export async function checkDomain(domain: string, enableFuzzyMatch = true): Promise { +export async function checkDomain(domain: string, enableFuzzyMatch: boolean = true): Promise { if (!domain) return defaultCheckResponse; clearDomainCheckCache(); @@ -31,13 +31,12 @@ export async function checkDomain(domain: string, enableFuzzyMatch = true): Prom if (!domainCheckCache.has(cacheKey)) { domainCheckCache.set(cacheKey, _checkDomain(domain, enableFuzzyMatch)); } - return domainCheckCache.get(domain) || defaultCheckResponse; + return domainCheckCache.get(cacheKey) || defaultCheckResponse; } - function _checkDomain(domain: string, enableFuzzyMatch: boolean): CheckDomainResult { const parsed = psl.parse(domain); - if (!parsed || !('domain' in parsed) || !parsed.domain) { + if (!parsed || !("domain" in parsed) || !parsed.domain) { const fallbackDomain = domain.split(".").slice(-2).join("."); return checkDomainInLists(domain, fallbackDomain, enableFuzzyMatch); } @@ -60,7 +59,7 @@ function checkDomainInLists(fullDomain: string, rootDomain: string, enableFuzzyM const rootDistance = levenshtein.get(fuzzyDomain, rootDomain); const minDistance = Math.min(fullDistance, rootDistance); if (minDistance <= DEFAULT_LEVENSHTEIN_TOLERANCE) { - fuzzyResult = { result: false, type: "unknown", extra: fuzzyDomain }; + fuzzyResult = { result: true, type: "fuzzy", extra: fuzzyDomain }; break; // Found a match, exit early } }