- Extension package: Manifest V3, background service worker, content scripts - Phishing detection engine with heuristic analysis (typosquatting, entropy, TLD, brand impersonation) - Local URL caching layer (Storage API) for <100ms cached lookups - Popup UI with protection status, stats, and phishing report button - Options page for settings management (blocked/allowed domains, feature toggles) - Server-side extension routes: URL check, phishing report, auth, stats, exposure check - Tier-aware feature gating (Basic/Plus/Premium) - 25 passing tests for phishing detection heuristics - Declarative net request rules for known phishing patterns - DarkWatch integration for credential exposure checks - Firefox compatibility layer via build modes Co-Authored-By: Paperclip <noreply@paperclip.ing>
123 lines
4.7 KiB
TypeScript
123 lines
4.7 KiB
TypeScript
import { BackgroundMessage, MessageType, PopupData } from '../types';
|
|
|
|
const popupView = document.getElementById('popup-view') as HTMLElement;
|
|
const blockedView = document.getElementById('blocked-view') as HTMLElement;
|
|
|
|
const protectionToggle = document.getElementById('protection-toggle') as HTMLElement;
|
|
const statusText = document.getElementById('status-text') as HTMLElement;
|
|
const accountStatus = document.getElementById('account-status') as HTMLElement;
|
|
const tierBadge = document.getElementById('tier-badge') as HTMLElement;
|
|
const threatsCount = document.getElementById('threats-count') as HTMLElement;
|
|
const urlsCount = document.getElementById('urls-count') as HTMLElement;
|
|
const lastThreat = document.getElementById('last-threat') as HTMLElement;
|
|
const threatDescription = document.getElementById('threat-description') as HTMLElement;
|
|
const blockingBadge = document.getElementById('blocking-badge') as HTMLElement;
|
|
const darkwatchBadge = document.getElementById('darkwatch-badge') as HTMLElement;
|
|
const realtimeBadge = document.getElementById('realtime-badge') as HTMLElement;
|
|
|
|
const reportBtn = document.getElementById('report-btn') as HTMLButtonElement;
|
|
const optionsBtn = document.getElementById('options-btn') as HTMLButtonElement;
|
|
const loginBtn = document.getElementById('login-btn') as HTMLButtonElement;
|
|
const continueBtn = document.getElementById('continue-btn') as HTMLButtonElement;
|
|
const backBtn = document.getElementById('back-btn') as HTMLButtonElement;
|
|
|
|
checkBlockedUrl();
|
|
loadPopupData();
|
|
|
|
function checkBlockedUrl(): void {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const blockedUrl = params.get('blocked');
|
|
if (blockedUrl) {
|
|
popupView.classList.add('hidden');
|
|
blockedView.classList.remove('hidden');
|
|
document.getElementById('blocked-url')!.textContent = blockedUrl;
|
|
|
|
continueBtn.onclick = () => {
|
|
chrome.tabs.update({ url: blockedUrl });
|
|
};
|
|
backBtn.onclick = () => {
|
|
chrome.tabs.goBack();
|
|
};
|
|
}
|
|
}
|
|
|
|
function loadPopupData(): void {
|
|
chrome.runtime.sendMessage({ type: MessageType.GET_POPUP_DATA }, (response) => {
|
|
const data = response as PopupData;
|
|
updateUI(data);
|
|
});
|
|
}
|
|
|
|
function updateUI(data: PopupData): void {
|
|
statusText.textContent = data.protectionEnabled ? 'Active' : 'Paused';
|
|
statusText.className = `status-value ${data.protectionEnabled ? 'safe' : 'warning'}`;
|
|
protectionToggle.className = `toggle ${data.protectionEnabled ? 'active' : ''}`;
|
|
|
|
accountStatus.textContent = data.isLoggedIn ? 'Connected' : 'Guest';
|
|
accountStatus.className = `status-value ${data.isLoggedIn ? 'safe' : ''}`;
|
|
|
|
const tier = data.tier || 'basic';
|
|
tierBadge.textContent = tier.charAt(0).toUpperCase() + tier.slice(1);
|
|
tierBadge.className = `tier-badge ${tier}`;
|
|
|
|
threatsCount.textContent = data.threatsBlockedToday.toLocaleString();
|
|
urlsCount.textContent = data.urlsCheckedToday.toLocaleString();
|
|
|
|
if (data.lastThreat) {
|
|
lastThreat.classList.remove('hidden');
|
|
threatDescription.textContent = data.lastThreat.description;
|
|
}
|
|
|
|
if (data.tier === 'plus' || data.tier === 'premium') {
|
|
blockingBadge.textContent = 'Active';
|
|
blockingBadge.className = 'tier-badge plus';
|
|
}
|
|
|
|
if (data.tier === 'premium') {
|
|
darkwatchBadge.textContent = 'Active';
|
|
darkwatchBadge.className = 'tier-badge plus';
|
|
realtimeBadge.textContent = 'Active';
|
|
realtimeBadge.className = 'tier-badge premium';
|
|
}
|
|
}
|
|
|
|
protectionToggle.addEventListener('click', () => {
|
|
chrome.runtime.sendMessage({ type: MessageType.TOGGLE_PROTECTION }, (response) => {
|
|
const enabled = (response as { enabled: boolean }).enabled;
|
|
protectionToggle.className = `toggle ${enabled ? 'active' : ''}`;
|
|
statusText.textContent = enabled ? 'Active' : 'Paused';
|
|
statusText.className = `status-value ${enabled ? 'safe' : 'warning'}`;
|
|
});
|
|
});
|
|
|
|
reportBtn.addEventListener('click', async () => {
|
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
if (!tab?.url) return;
|
|
|
|
const title = tab.title || 'Unknown Page';
|
|
const success = await chrome.runtime.sendMessage({
|
|
type: MessageType.REPORT_PHISHING,
|
|
payload: {
|
|
url: tab.url,
|
|
pageTitle: title,
|
|
tabId: tab.id,
|
|
timestamp: Date.now(),
|
|
reason: 'Manual report from popup',
|
|
heuristics: {},
|
|
},
|
|
});
|
|
|
|
reportBtn.textContent = (success as { success: boolean })?.success
|
|
? '✓ Reported'
|
|
: '⚡ Report Phishing';
|
|
setTimeout(() => { reportBtn.innerHTML = '<span>⚡</span> Report Phishing'; }, 2000);
|
|
});
|
|
|
|
optionsBtn.addEventListener('click', () => {
|
|
chrome.tabs.create({ url: chrome.runtime.getURL('options.html') });
|
|
});
|
|
|
|
loginBtn.addEventListener('click', () => {
|
|
chrome.tabs.create({ url: 'https://app.shieldai.com/auth/login?extension=true' });
|
|
});
|