Files
ShieldAI/packages/extension/src/popup/popup.ts
Michael Freno de0ddac65d Add ShieldAI browser extension with phishing & spam detection (FRE-4576)
- 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>
2026-05-09 21:53:29 -04:00

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' });
});