diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 5ad7cf80f31..587506a10d0 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -452,6 +452,8 @@ export default class MainBackground { this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService); this.storageService = new BrowserLocalStorageService(this.logService); + PhishingDetectionService.Initialize(this.logService); + this.intraprocessMessagingSubject = new Subject>>(); this.messagingService = MessageSender.combine( diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 1e2ac1812ca..af4f271427e 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -30,6 +30,12 @@ "matches": ["*://*/*", "file:///*"], "exclude_matches": ["*://*/*.xml*", "file:///*.xml*"], "run_at": "document_start" + }, + { + "js": ["content/trigger-phishing-detection-script-injection.js"], + "matches": ["*://*/*", "file:///*"], + "exclude_matches": ["*://*/*.xml*", "file:///*.xml*"], + "run_at": "document_start" } ], "background": { diff --git a/apps/browser/src/phishing-detection/content/phishing-detection-browser.service.ts b/apps/browser/src/phishing-detection/content/phishing-detection-browser.service.ts new file mode 100644 index 00000000000..714f3e3f9b2 --- /dev/null +++ b/apps/browser/src/phishing-detection/content/phishing-detection-browser.service.ts @@ -0,0 +1,46 @@ +export class PhishingDetectionBrowserService { + static notifyUser(url: string) { + const phishingDivId = "phishing-notification-bar"; + const message = `${url} is a known phishing site`; + + const wrapper = document.createElement("div"); + wrapper.id = phishingDivId; + wrapper.classList.add("inner-wrapper"); + + wrapper.style.position = "fixed"; + wrapper.style.top = "20px"; + wrapper.style.right = "20px"; + wrapper.style.zIndex = "10000"; + wrapper.style.backgroundColor = "#fff"; + wrapper.style.padding = "15px"; + wrapper.style.border = "1px solid #ccc"; + wrapper.style.borderRadius = "5px"; + wrapper.style.boxShadow = "0 2px 10px rgba(0,0,0,0.2)"; + + const messageElement = document.createElement("div"); + messageElement.id = "change-text"; + messageElement.classList.add("notification-body"); + messageElement.textContent = message; + + const exitButton = document.createElement("button"); + exitButton.type = "button"; + exitButton.id = "change-exit"; + exitButton.classList.add("primary"); + exitButton.textContent = "Exit the page"; + + wrapper.appendChild(messageElement); + wrapper.appendChild(exitButton); + + document.body.appendChild(wrapper); + + setTimeout(() => { + if (document.body.contains(wrapper)) { + document.body.removeChild(wrapper); + } + }, 10000); + } + + static getActiveUrl() { + return window?.location?.href; + } +} diff --git a/apps/browser/src/phishing-detection/phishing-detection.enum.ts b/apps/browser/src/phishing-detection/phishing-detection.enum.ts new file mode 100644 index 00000000000..0bea27af8b9 --- /dev/null +++ b/apps/browser/src/phishing-detection/phishing-detection.enum.ts @@ -0,0 +1,3 @@ +export enum PhishingDetectionCommands { + CheckUrl = "CheckUrl", +} diff --git a/apps/browser/src/phishing-detection/trigger-phishing-detection-script-injection.ts b/apps/browser/src/phishing-detection/trigger-phishing-detection-script-injection.ts new file mode 100644 index 00000000000..319c7e50be8 --- /dev/null +++ b/apps/browser/src/phishing-detection/trigger-phishing-detection-script-injection.ts @@ -0,0 +1,31 @@ +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; + +import { PhishingDetectionBrowserService } from "./content/phishing-detection-browser.service"; +import { PhishingDetectionCommands } from "./phishing-detection.enum"; + +const isDev = process.env.ENV === "development"; +const logService = new ConsoleLogService(isDev); + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", loadPhishingDetectionContent); +} else { + void loadPhishingDetectionContent(); +} + +async function loadPhishingDetectionContent() { + const activeUrl = PhishingDetectionBrowserService.getActiveUrl(); + + const { isPhishingDomain } = await chrome.runtime.sendMessage({ + command: PhishingDetectionCommands.CheckUrl, + activeUrl, + }); + + if (isPhishingDomain) { + const domain = Utils.getDomain(activeUrl); + + PhishingDetectionBrowserService.notifyUser(domain); + } +} + +logService.info("Phishing Detection Service loaded."); diff --git a/apps/browser/src/platform/services/phishing-detection.service.ts b/apps/browser/src/platform/services/phishing-detection.service.ts index 8ef3ef311a5..5a1139d7640 100644 --- a/apps/browser/src/platform/services/phishing-detection.service.ts +++ b/apps/browser/src/platform/services/phishing-detection.service.ts @@ -1,3 +1,4 @@ +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Subscription } from "rxjs"; import { PhishingApiServiceAbstraction } from "@bitwarden/common/abstractions/phishing-api.service.abstraction"; @@ -7,10 +8,20 @@ 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 { PhishingDetectionCommands } from "../../phishing-detection/phishing-detection.enum"; import { BrowserApi } from "../browser/browser-api"; export class PhishingDetectionService { private static knownPhishingDomains = new Set(); + static logService: LogService; + + static Initialize(logService: LogService) { + PhishingDetectionService.logService = logService; + PhishingDetectionService.setupCheckUrlListener(); + + // Initializing the data for local development + PhishingDetectionService.loadMockedData(); + } 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 @@ -229,9 +240,8 @@ export class PhishingDetectionService { const activeUrl = await PhishingDetectionService.getActiveUrl(); const isPhishingDomain = PhishingDetectionService.checkUrl(activeUrl); - if (isPhishingDomain) { - PhishingDetectionService.notifyUser(activeUrl); - } + PhishingDetectionService.logService.debug("CheckUrl handler", { result, message }); + sendResponse(result); } }); } diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index ff5331ae459..77fab0090aa 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -199,6 +199,8 @@ const mainConfig = { "./src/autofill/content/bootstrap-autofill-overlay-notifications.ts", "content/bootstrap-legacy-autofill-overlay": "./src/autofill/deprecated/content/bootstrap-legacy-autofill-overlay.ts", + "content/trigger-phishing-detection-script-injection": + "./src/phishing-detection/trigger-phishing-detection-script-injection.ts", "content/autofiller": "./src/autofill/content/autofiller.ts", "content/auto-submit-login": "./src/autofill/content/auto-submit-login.ts", "content/notificationBar": "./src/autofill/content/notification-bar.ts",