1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 05:30:01 +00:00

Get the known phishing domain from the server

This commit is contained in:
Cy Okeke
2025-03-14 16:26:32 +01:00
parent a5450cef4a
commit c2f2c36856
3 changed files with 141 additions and 16 deletions

View File

@@ -272,6 +272,8 @@ import BrowserMemoryStorageService from "../platform/services/browser-memory-sto
import { BrowserScriptInjectorService } from "../platform/services/browser-script-injector.service";
import I18nService from "../platform/services/i18n.service";
import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service";
import { PhishingApiService } from "../platform/services/phishing-api.service";
import { PhishingDetectionService } from "../platform/services/phishing-detection.service";
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service";
@@ -710,6 +712,15 @@ export default class MainBackground {
this.vaultTimeoutSettingsService,
);
// Initialize phishing detection services
const phishingApiService = new PhishingApiService(this.apiService);
PhishingDetectionService.initialize(
phishingApiService,
this.logService,
this.storageService,
this.taskSchedulerService,
);
this.fileUploadService = new FileUploadService(this.logService, this.apiService);
this.cipherFileUploadService = new CipherFileUploadService(
this.apiService,
@@ -1327,6 +1338,10 @@ export default class MainBackground {
this.syncServiceListener?.listener$().subscribe();
await this.autoSubmitLoginBackground.init();
// Set up phishing detection tab event listeners
const phishingDetectionService = new PhishingDetectionService();
phishingDetectionService.setupTabEventListeners();
if (
BrowserApi.isManifestVersion(2) &&
(await this.configService.getFeatureFlag(FeatureFlag.PM4154_BulkEncryptionService))

View File

@@ -1,23 +1,139 @@
import { Subscription } from "rxjs";
import { PhishingApiServiceAbstraction } from "@bitwarden/common/abstractions/phishing-api.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { ScheduledTaskNames } from "@bitwarden/common/platform/scheduling";
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling/task-scheduler.service";
import { BrowserApi } from "../browser/browser-api";
export class PhishingDetectionService {
private static knownPhishingDomains = new Set();
private static knownPhishingDomains = new Set<string>();
private static lastUpdateTime: number = 0;
private static readonly UPDATE_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
private static readonly STORAGE_KEY = "phishing_domains_cache";
private static phishingApiService: PhishingApiServiceAbstraction;
private static logService: LogService;
private static storageService: AbstractStorageService;
private static taskSchedulerService: TaskSchedulerService;
private static updateCacheSubscription: Subscription | null = null;
private static isUpdating = false;
static initialize(
phishingApiService: PhishingApiServiceAbstraction,
logService: LogService,
storageService: AbstractStorageService,
taskSchedulerService: TaskSchedulerService,
) {
PhishingDetectionService.phishingApiService = phishingApiService;
PhishingDetectionService.logService = logService;
PhishingDetectionService.storageService = storageService;
PhishingDetectionService.taskSchedulerService = taskSchedulerService;
// Register the update task
this.taskSchedulerService.registerTaskHandler(
ScheduledTaskNames.phishingDomainUpdate,
async () => {
try {
await this.updateKnownPhishingDomains();
} catch (error) {
this.logService.error("Failed to update phishing domains in task handler:", error);
}
},
);
// Initial load of cached domains
void this.loadCachedDomains();
// Set up periodic updates every 24 hours
this.setupPeriodicUpdates();
}
private static setupPeriodicUpdates() {
// Clean up any existing subscription
if (this.updateCacheSubscription) {
this.updateCacheSubscription.unsubscribe();
}
this.updateCacheSubscription = this.taskSchedulerService.setInterval(
ScheduledTaskNames.phishingDomainUpdate,
this.UPDATE_INTERVAL,
);
}
private static async loadCachedDomains() {
try {
const cachedData = await this.storageService.get<{ domains: string[]; timestamp: number }>(
this.STORAGE_KEY,
);
if (cachedData) {
this.knownPhishingDomains = new Set(cachedData.domains);
this.lastUpdateTime = cachedData.timestamp;
}
// If cache is empty or expired, trigger an immediate update
if (
this.knownPhishingDomains.size === 0 ||
Date.now() - this.lastUpdateTime >= this.UPDATE_INTERVAL
) {
await this.updateKnownPhishingDomains();
}
} catch (error) {
this.logService.error("Failed to load cached phishing domains:", error);
}
}
static checkUrl(url: string): boolean {
const domain = Utils.getDomain(url);
return PhishingDetectionService.knownPhishingDomains.has(domain);
return domain ? PhishingDetectionService.knownPhishingDomains.has(domain) : false;
}
// @TODO: We need to flesh this out to actually use the real data that comes from the server.
// This method can be run using a background worker once a day or at a similar interval.
static updateKnownPhishingDomains(): void {}
static async updateKnownPhishingDomains(): Promise<void> {
// Prevent concurrent updates
if (this.isUpdating) {
this.logService.warning("Update already in progress, skipping...");
return;
}
// @TODO: This can be remove once we implement the real code.
static loadMockedData() {
PhishingDetectionService.knownPhishingDomains.add("google.com");
PhishingDetectionService.knownPhishingDomains.add("atlassian.net");
this.isUpdating = true;
try {
const domains = await PhishingDetectionService.phishingApiService.getKnownPhishingDomains();
// Clear old domains to prevent memory leaks
PhishingDetectionService.knownPhishingDomains.clear();
// Add new domains
domains.forEach((domain: string) => {
if (domain) {
// Only add valid domains
PhishingDetectionService.knownPhishingDomains.add(domain);
}
});
PhishingDetectionService.lastUpdateTime = Date.now();
// Cache the updated domains
await this.storageService.save(this.STORAGE_KEY, {
domains: Array.from(this.knownPhishingDomains),
timestamp: this.lastUpdateTime,
});
} catch (error) {
PhishingDetectionService.logService.error("Failed to update phishing domains:", error);
} finally {
this.isUpdating = false;
}
}
static cleanup() {
if (this.updateCacheSubscription) {
this.updateCacheSubscription.unsubscribe();
this.updateCacheSubscription = null;
}
this.knownPhishingDomains.clear();
this.lastUpdateTime = 0;
this.isUpdating = false;
}
static async getActiveUrl(): Promise<string> {
@@ -38,10 +154,6 @@ export class PhishingDetectionService {
BrowserApi.addListener(chrome.tabs.onUpdated, async (tabId, changeInfo, tab) => {
if (changeInfo.status === "complete") {
const activeUrl = await PhishingDetectionService.getActiveUrl();
// Debugging
console.log("Tab changed:", { tab, changeInfo, tabId });
const isPhishingDomain = PhishingDetectionService.checkUrl(activeUrl);
if (isPhishingDomain) {
@@ -51,6 +163,3 @@ export class PhishingDetectionService {
});
}
}
// Initializing the data for local development
PhishingDetectionService.loadMockedData();

View File

@@ -8,6 +8,7 @@ export const ScheduledTaskNames = {
eventUploadsInterval: "eventUploadsInterval",
vaultTimeoutCheckInterval: "vaultTimeoutCheckInterval",
clearPopupViewCache: "clearPopupViewCache",
phishingDomainUpdate: "phishingDomainUpdate",
} as const;
export type ScheduledTaskName = (typeof ScheduledTaskNames)[keyof typeof ScheduledTaskNames];