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:
117
packages/extension/src/lib/settings.ts
Normal file
117
packages/extension/src/lib/settings.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { ExtensionSettings, SubscriptionTier, TIER_FEATURES, MessageType } from '../types';
|
||||
|
||||
const DEFAULT_SETTINGS: ExtensionSettings = {
|
||||
apiKey: '',
|
||||
apiBaseUrl: 'https://api.shieldai.com',
|
||||
authToken: null,
|
||||
userId: null,
|
||||
tier: null,
|
||||
enabled: true,
|
||||
activeBlocking: false,
|
||||
darkWatchEnabled: false,
|
||||
spamProtectionEnabled: true,
|
||||
showNotifications: true,
|
||||
blockedDomains: [],
|
||||
allowedDomains: [],
|
||||
lastSyncAt: null,
|
||||
};
|
||||
|
||||
export class SettingsManager {
|
||||
private settings: ExtensionSettings = { ...DEFAULT_SETTINGS };
|
||||
private loaded = false;
|
||||
|
||||
async load(): Promise<ExtensionSettings> {
|
||||
if (this.loaded) return this.settings;
|
||||
|
||||
const stored = await chrome.storage.sync.get('shieldaiSettings');
|
||||
if (stored.shieldaiSettings) {
|
||||
this.settings = { ...DEFAULT_SETTINGS, ...stored.shieldaiSettings };
|
||||
}
|
||||
this.loaded = true;
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
async get(): Promise<ExtensionSettings> {
|
||||
if (!this.loaded) await this.load();
|
||||
return { ...this.settings };
|
||||
}
|
||||
|
||||
async update(partial: Partial<ExtensionSettings>): Promise<ExtensionSettings> {
|
||||
await this.load();
|
||||
this.settings = { ...this.settings, ...partial };
|
||||
await chrome.storage.sync.set({ shieldaiSettings: this.settings });
|
||||
return { ...this.settings };
|
||||
}
|
||||
|
||||
async getAuthToken(): Promise<string | null> {
|
||||
await this.load();
|
||||
return this.settings.authToken;
|
||||
}
|
||||
|
||||
async isLoggedIn(): Promise<boolean> {
|
||||
await this.load();
|
||||
return this.settings.authToken !== null && this.settings.userId !== null;
|
||||
}
|
||||
|
||||
async getTier(): Promise<SubscriptionTier | null> {
|
||||
await this.load();
|
||||
return this.settings.tier;
|
||||
}
|
||||
|
||||
async getFeatures(): Promise<typeof TIER_FEATURES[SubscriptionTier]> {
|
||||
const tier = await this.getTier();
|
||||
if (tier) return TIER_FEATURES[tier];
|
||||
return TIER_FEATURES[SubscriptionTier.BASIC];
|
||||
}
|
||||
|
||||
async isDomainBlocked(domain: string): Promise<boolean> {
|
||||
await this.load();
|
||||
return this.settings.blockedDomains.some(
|
||||
(d) => d.toLowerCase() === domain.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
async isDomainAllowed(domain: string): Promise<boolean> {
|
||||
await this.load();
|
||||
return this.settings.allowedDomains.some(
|
||||
(d) => d.toLowerCase() === domain.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
async isProtectionEnabled(): Promise<boolean> {
|
||||
await this.load();
|
||||
return this.settings.enabled;
|
||||
}
|
||||
|
||||
async toggleProtection(): Promise<boolean> {
|
||||
await this.load();
|
||||
this.settings.enabled = !this.settings.enabled;
|
||||
await chrome.storage.sync.set({ shieldaiSettings: this.settings });
|
||||
return this.settings.enabled;
|
||||
}
|
||||
|
||||
async addBlockedDomain(domain: string): Promise<void> {
|
||||
await this.load();
|
||||
const lower = domain.toLowerCase();
|
||||
if (!this.settings.blockedDomains.includes(lower)) {
|
||||
this.settings.blockedDomains.push(lower);
|
||||
await chrome.storage.sync.set({ shieldaiSettings: this.settings });
|
||||
}
|
||||
}
|
||||
|
||||
async removeBlockedDomain(domain: string): Promise<void> {
|
||||
await this.load();
|
||||
this.settings.blockedDomains = this.settings.blockedDomains.filter(
|
||||
(d) => d !== domain.toLowerCase()
|
||||
);
|
||||
await chrome.storage.sync.set({ shieldaiSettings: this.settings });
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
this.settings = { ...DEFAULT_SETTINGS };
|
||||
this.loaded = true;
|
||||
await chrome.storage.sync.set({ shieldaiSettings: this.settings });
|
||||
}
|
||||
}
|
||||
|
||||
export const settingsManager = new SettingsManager();
|
||||
Reference in New Issue
Block a user