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>
This commit is contained in:
122
packages/extension/src/popup/popup.ts
Normal file
122
packages/extension/src/popup/popup.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
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' });
|
||||
});
|
||||
Reference in New Issue
Block a user