From 319d8278597b0c73564688e2ffaaddc9bc404fe2 Mon Sep 17 00:00:00 2001 From: Cy Okeke Date: Mon, 17 Mar 2025 12:36:30 +0100 Subject: [PATCH] move the call to background --- .../browser/src/background/main.background.ts | 17 +--- .../platform/services/phishing-api.service.ts | 2 +- .../services/phishing-detection.service.ts | 79 ++++++++++++++++++- 3 files changed, 78 insertions(+), 20 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 66efb3568fd..5ad7cf80f31 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -78,7 +78,6 @@ import { ClientType } from "@bitwarden/common/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { BulkEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/bulk-encrypt.service.implementation"; import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; import { FallbackBulkEncryptService } from "@bitwarden/common/key-management/crypto/services/fallback-bulk-encrypt.service"; import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/multithread-encrypt.service.implementation"; @@ -712,7 +711,6 @@ export default class MainBackground { this.vaultTimeoutSettingsService, ); - // Initialize phishing detection services const phishingApiService = new PhishingApiService(this.apiService); PhishingDetectionService.initialize( phishingApiService, @@ -1334,23 +1332,10 @@ export default class MainBackground { this.commandsBackground.init(); this.contextMenusBackground?.init(); this.idleBackground.init(); - this.webRequestBackground?.startListening(); + await this.webRequestBackground?.startListening(); 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)) - ) { - await this.bulkEncryptService.setFeatureFlagEncryptService( - new BulkEncryptServiceImplementation(this.cryptoFunctionService, this.logService), - ); - } - // If the user is logged out, switch to the next account const active = await firstValueFrom(this.accountService.activeAccount$); if (active != null) { diff --git a/apps/browser/src/platform/services/phishing-api.service.ts b/apps/browser/src/platform/services/phishing-api.service.ts index be2d6b49287..f02402e093a 100644 --- a/apps/browser/src/platform/services/phishing-api.service.ts +++ b/apps/browser/src/platform/services/phishing-api.service.ts @@ -5,7 +5,7 @@ export class PhishingApiService implements PhishingApiServiceAbstraction { constructor(private apiService: ApiService) {} async getKnownPhishingDomains(): Promise { - const response = await this.apiService.send("GET", "/phishing/domains", null, true, true); + const response = await this.apiService.send("GET", "/phishing-domains", null, false, true); return response as string[]; } } diff --git a/apps/browser/src/platform/services/phishing-detection.service.ts b/apps/browser/src/platform/services/phishing-detection.service.ts index 20032441ceb..8ef3ef311a5 100644 --- a/apps/browser/src/platform/services/phishing-detection.service.ts +++ b/apps/browser/src/platform/services/phishing-detection.service.ts @@ -13,13 +13,17 @@ export class PhishingDetectionService { private static knownPhishingDomains = new Set(); private static lastUpdateTime: number = 0; private static readonly UPDATE_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours in milliseconds + private static readonly RETRY_INTERVAL = 5 * 60 * 1000; // 5 minutes + private static readonly MAX_RETRIES = 3; 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 retrySubscription: Subscription | null = null; private static isUpdating = false; + private static retryCount = 0; static initialize( phishingApiService: PhishingApiServiceAbstraction, @@ -49,13 +53,19 @@ export class PhishingDetectionService { // Set up periodic updates every 24 hours this.setupPeriodicUpdates(); + + // Set up tab listener + this.setupTabEventListeners(); } private static setupPeriodicUpdates() { - // Clean up any existing subscription + // Clean up any existing subscriptions if (this.updateCacheSubscription) { this.updateCacheSubscription.unsubscribe(); } + if (this.retrySubscription) { + this.retrySubscription.unsubscribe(); + } this.updateCacheSubscription = this.taskSchedulerService.setInterval( ScheduledTaskNames.phishingDomainUpdate, @@ -63,6 +73,39 @@ export class PhishingDetectionService { ); } + private static scheduleRetry() { + // If we've exceeded max retries, stop retrying + if (this.retryCount >= this.MAX_RETRIES) { + this.logService.warning( + `Max retries (${this.MAX_RETRIES}) reached for phishing domain update. Will try again in ${this.UPDATE_INTERVAL / (1000 * 60 * 60)} hours.`, + ); + this.retryCount = 0; + if (this.retrySubscription) { + this.retrySubscription.unsubscribe(); + this.retrySubscription = null; + } + return; + } + + // Clean up existing retry subscription if any + if (this.retrySubscription) { + this.retrySubscription.unsubscribe(); + } + + // Increment retry count + this.retryCount++; + + // Schedule a retry in 5 minutes + this.retrySubscription = this.taskSchedulerService.setInterval( + ScheduledTaskNames.phishingDomainUpdate, + this.RETRY_INTERVAL, + ); + + this.logService.info( + `Scheduled retry ${this.retryCount}/${this.MAX_RETRIES} for phishing domain update in ${this.RETRY_INTERVAL / (1000 * 60)} minutes`, + ); + } + private static async loadCachedDomains() { try { const cachedData = await this.storageService.get<{ domains: string[]; timestamp: number }>( @@ -99,7 +142,9 @@ export class PhishingDetectionService { this.isUpdating = true; try { + this.logService.info("Starting phishing domains update..."); const domains = await PhishingDetectionService.phishingApiService.getKnownPhishingDomains(); + this.logService.info("Received phishing domains response"); // Clear old domains to prevent memory leaks PhishingDetectionService.knownPhishingDomains.clear(); @@ -119,8 +164,31 @@ export class PhishingDetectionService { domains: Array.from(this.knownPhishingDomains), timestamp: this.lastUpdateTime, }); + + // Reset retry count and clear retry subscription on success + this.retryCount = 0; + if (this.retrySubscription) { + this.retrySubscription.unsubscribe(); + this.retrySubscription = null; + } + + this.logService.info( + `Successfully updated phishing domains cache with ${this.knownPhishingDomains.size} domains`, + ); } catch (error) { - PhishingDetectionService.logService.error("Failed to update phishing domains:", error); + this.logService.error("Error details:", error); + if ( + error?.message?.includes("Access token not found") || + error?.message?.includes("Failed to decode access token") + ) { + this.logService.info( + "Authentication required for phishing domain update, will retry when authenticated", + ); + this.scheduleRetry(); + } else { + PhishingDetectionService.logService.error("Failed to update phishing domains:", error); + throw error; + } } finally { this.isUpdating = false; } @@ -131,9 +199,14 @@ export class PhishingDetectionService { this.updateCacheSubscription.unsubscribe(); this.updateCacheSubscription = null; } + if (this.retrySubscription) { + this.retrySubscription.unsubscribe(); + this.retrySubscription = null; + } this.knownPhishingDomains.clear(); this.lastUpdateTime = 0; this.isUpdating = false; + this.retryCount = 0; } static async getActiveUrl(): Promise { @@ -150,7 +223,7 @@ export class PhishingDetectionService { /* This listener will check the URL when the tab has finished loading. */ - setupTabEventListeners(): void { + private static setupTabEventListeners(): void { BrowserApi.addListener(chrome.tabs.onUpdated, async (tabId, changeInfo, tab) => { if (changeInfo.status === "complete") { const activeUrl = await PhishingDetectionService.getActiveUrl();