diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts
index e40019d99b6..a28b8730109 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts
@@ -91,6 +91,11 @@ describe("AutofillConfirmationDialogComponent", () => {
jest.resetAllMocks();
});
+ const findShowAll = (inFx?: ComponentFixture
) =>
+ (inFx || fixture).nativeElement.querySelector(
+ "button.tw-text-sm.tw-font-medium.tw-cursor-pointer",
+ ) as HTMLButtonElement | null;
+
it("normalizes currentUrl and savedUrls via Utils.getHostname", () => {
expect(Utils.getHostname).toHaveBeenCalledTimes(1 + (params.savedUrls?.length ?? 0));
expect(component.currentUrl()).toBe("example.com");
@@ -191,21 +196,47 @@ describe("AutofillConfirmationDialogComponent", () => {
expect(text).toContain("two.example.com");
});
- it("shows the 'view all' button when savedUrls > 1 and toggles the button text when clicked", () => {
- const findViewAll = () =>
- fixture.nativeElement.querySelector(
- "button.tw-text-sm.tw-font-medium.tw-cursor-pointer",
- ) as HTMLButtonElement | null;
-
- let btn = findViewAll();
+ it("shows the 'show all' button when savedUrls > 1", () => {
+ const btn = findShowAll();
expect(btn).toBeTruthy();
+ expect(btn!.textContent).toContain("showAll");
+ });
+ it('hides the "show all" button when savedUrls is empty', async () => {
+ const newParams: AutofillConfirmationDialogParams = {
+ currentUrl: "https://bitwarden.com/help",
+ savedUrls: [],
+ };
+
+ const { fixture: vf } = await createFreshFixture({ params: newParams });
+ vf.detectChanges();
+ const btn = findShowAll(vf);
+ expect(btn).toBeNull();
+ });
+
+ it("handles toggling of the 'show all' button correctly", async () => {
+ const { fixture: vf, component: vc } = await createFreshFixture();
+
+ let btn = findShowAll(vf);
+ expect(btn).toBeTruthy();
+ expect(vc.savedUrlsExpanded()).toBe(false);
+ expect(btn!.textContent).toContain("showAll");
+
+ // click to expand
btn!.click();
- fixture.detectChanges();
+ vf.detectChanges();
- btn = findViewAll();
- expect(btn!.textContent).toContain("viewLess");
- expect(component.savedUrlsExpanded()).toBe(true);
+ btn = findShowAll(vf);
+ expect(btn!.textContent).toContain("showLess");
+ expect(vc.savedUrlsExpanded()).toBe(true);
+
+ // click to collapse
+ btn!.click();
+ vf.detectChanges();
+
+ btn = findShowAll(vf);
+ expect(btn!.textContent).toContain("showAll");
+ expect(vc.savedUrlsExpanded()).toBe(false);
});
it("shows autofillWithoutAdding text on autofill button when viewOnly is false", () => {
From 3de3bee08ff8d77b0333997b7c60202accd824f4 Mon Sep 17 00:00:00 2001
From: Daniel Riera
Date: Tue, 25 Nov 2025 13:42:46 -0500
Subject: [PATCH 12/13] [PM-27821]Add validation of extension origin for uses
of window.postMessage (#17476)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* PM-27821 - Replace chrome.runtime.getURL() with BrowserApi.getRuntimeURL() for consistency
- Add extension origin validation for all window.postMessage calls
- Implement token-based authentication for inline menu communications
- Add message source validation (event.source === globalThis.parent)
- Add command presence validation (- Update notification bar to validate message origins and commands
- Add extensionOrigin property to services using postMessage
- Generate session tokens for inline menu containers (32-char random)
- Validate tokens in message handlers to prevent unauthorized commands
* Add explicit token validation
* only set when receiving the trusted initNotificationBar message
* await windowmessageorigin before posting to parent
* fix tests
* the parent must include its origin in the message for notification bar race condition
* reduce if statements to one block and comment
* extract parentOrigin from the URL and set windoMessageOrigin accordingly
* consolidate if statements
* add bar.spec file
* fix merge conflict
---
.../background/notification.background.ts | 2 +-
.../autofill/background/overlay.background.ts | 6 +-
.../content/content-message-handler.spec.ts | 14 +-
.../abstractions/notification-bar.ts | 1 +
.../src/autofill/notification/bar.spec.ts | 121 ++++++++++++++++
apps/browser/src/autofill/notification/bar.ts | 34 ++++-
.../autofill-inline-menu-button.ts | 1 +
.../autofill-inline-menu-container.ts | 2 +-
.../abstractions/autofill-inline-menu-list.ts | 1 +
...utofill-inline-menu-iframe.service.spec.ts | 12 +-
.../autofill-inline-menu-iframe.service.ts | 10 +-
.../autofill-inline-menu-button.spec.ts | 21 +--
.../list/autofill-inline-menu-list.spec.ts | 129 ++++++++++++------
.../autofill-inline-menu-container.ts | 11 +-
.../autofill-inline-menu-page-element.ts | 42 ++++--
...notifications-content.service.spec.ts.snap | 2 +-
...rlay-notifications-content.service.spec.ts | 5 +-
.../overlay-notifications-content.service.ts | 16 ++-
.../src/autofill/spec/autofill-mocks.ts | 2 +
.../src/autofill/spec/testing-utils.ts | 8 +-
20 files changed, 344 insertions(+), 96 deletions(-)
create mode 100644 apps/browser/src/autofill/notification/bar.spec.ts
diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts
index de1514f0342..547c5ba1575 100644
--- a/apps/browser/src/autofill/background/notification.background.ts
+++ b/apps/browser/src/autofill/background/notification.background.ts
@@ -1344,7 +1344,7 @@ export default class NotificationBackground {
return;
}
- const extensionUrl = chrome.runtime.getURL("popup/index.html");
+ const extensionUrl = BrowserApi.getRuntimeURL("popup/index.html");
const unlockPopoutTabs = (await BrowserApi.tabsQuery({ url: `${extensionUrl}*` })).filter(
(tab) => tab.url?.includes(`singleActionPopout=${AuthPopoutType.unlockExtension}`),
);
diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts
index 0eb7d070de3..af8141f1ab8 100644
--- a/apps/browser/src/autofill/background/overlay.background.ts
+++ b/apps/browser/src/autofill/background/overlay.background.ts
@@ -2949,13 +2949,13 @@ export class OverlayBackground implements OverlayBackgroundInterface {
(await this.checkFocusedFieldHasValue(port.sender.tab)) &&
(await this.shouldShowSaveLoginInlineMenuList(port.sender.tab));
- const iframeUrl = chrome.runtime.getURL(
+ const iframeUrl = BrowserApi.getRuntimeURL(
`overlay/menu-${isInlineMenuListPort ? "list" : "button"}.html`,
);
- const styleSheetUrl = chrome.runtime.getURL(
+ const styleSheetUrl = BrowserApi.getRuntimeURL(
`overlay/menu-${isInlineMenuListPort ? "list" : "button"}.css`,
);
- const extensionOrigin = new URL(iframeUrl).origin;
+ const extensionOrigin = iframeUrl ? new URL(iframeUrl).origin : null;
this.postMessageToPort(port, {
command: `initAutofillInlineMenu${isInlineMenuListPort ? "List" : "Button"}`,
diff --git a/apps/browser/src/autofill/content/content-message-handler.spec.ts b/apps/browser/src/autofill/content/content-message-handler.spec.ts
index fe023f344d6..874e1cc76ff 100644
--- a/apps/browser/src/autofill/content/content-message-handler.spec.ts
+++ b/apps/browser/src/autofill/content/content-message-handler.spec.ts
@@ -56,7 +56,11 @@ describe("ContentMessageHandler", () => {
});
it("sends an authResult message", () => {
- postWindowMessage({ command: "authResult", lastpass: true, code: "code", state: "state" });
+ postWindowMessage(
+ { command: "authResult", lastpass: true, code: "code", state: "state" },
+ "https://localhost/",
+ window,
+ );
expect(sendMessageSpy).toHaveBeenCalledWith({
command: "authResult",
@@ -68,7 +72,11 @@ describe("ContentMessageHandler", () => {
});
it("sends a webAuthnResult message", () => {
- postWindowMessage({ command: "webAuthnResult", data: "data", remember: true });
+ postWindowMessage(
+ { command: "webAuthnResult", data: "data", remember: true },
+ "https://localhost/",
+ window,
+ );
expect(sendMessageSpy).toHaveBeenCalledWith({
command: "webAuthnResult",
@@ -82,7 +90,7 @@ describe("ContentMessageHandler", () => {
const mockCode = "mockCode";
const command = "duoResult";
- postWindowMessage({ command: command, code: mockCode });
+ postWindowMessage({ command: command, code: mockCode }, "https://localhost/", window);
expect(sendMessageSpy).toHaveBeenCalledWith({
command: command,
diff --git a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts
index 7881d2f1cac..b23c3c17abb 100644
--- a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts
+++ b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts
@@ -51,6 +51,7 @@ type NotificationBarWindowMessage = {
};
error?: string;
initData?: NotificationBarIframeInitData;
+ parentOrigin?: string;
};
type NotificationBarWindowMessageHandlers = {
diff --git a/apps/browser/src/autofill/notification/bar.spec.ts b/apps/browser/src/autofill/notification/bar.spec.ts
new file mode 100644
index 00000000000..ae60e2efc91
--- /dev/null
+++ b/apps/browser/src/autofill/notification/bar.spec.ts
@@ -0,0 +1,121 @@
+import { mock } from "jest-mock-extended";
+
+import { postWindowMessage } from "../spec/testing-utils";
+
+import { NotificationBarWindowMessage } from "./abstractions/notification-bar";
+import "./bar";
+
+jest.mock("lit", () => ({ render: jest.fn() }));
+jest.mock("@lit-labs/signals", () => ({
+ signal: jest.fn((testValue) => ({ get: (): typeof testValue => testValue })),
+}));
+jest.mock("../content/components/notification/container", () => ({
+ NotificationContainer: jest.fn(),
+}));
+
+describe("NotificationBar iframe handleWindowMessage security", () => {
+ const trustedOrigin = "http://localhost";
+ const maliciousOrigin = "https://malicious.com";
+
+ const createMessage = (
+ overrides: Partial = {},
+ ): NotificationBarWindowMessage => ({
+ command: "initNotificationBar",
+ ...overrides,
+ });
+
+ beforeEach(() => {
+ Object.defineProperty(globalThis, "location", {
+ value: { search: `?parentOrigin=${encodeURIComponent(trustedOrigin)}` },
+ writable: true,
+ configurable: true,
+ });
+ Object.defineProperty(globalThis, "parent", {
+ value: mock(),
+ writable: true,
+ configurable: true,
+ });
+ globalThis.dispatchEvent(new Event("load"));
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it.each([
+ {
+ description: "not from parent window",
+ message: () => createMessage(),
+ origin: trustedOrigin,
+ source: () => mock(),
+ },
+ {
+ description: "with mismatched origin",
+ message: () => createMessage(),
+ origin: maliciousOrigin,
+ source: () => globalThis.parent,
+ },
+ {
+ description: "without command field",
+ message: () => ({}),
+ origin: trustedOrigin,
+ source: () => globalThis.parent,
+ },
+ {
+ description: "initNotificationBar with mismatched parentOrigin",
+ message: () => createMessage({ parentOrigin: maliciousOrigin }),
+ origin: trustedOrigin,
+ source: () => globalThis.parent,
+ },
+ {
+ description: "when windowMessageOrigin is not set",
+ message: () => createMessage(),
+ origin: "different-origin",
+ source: () => globalThis.parent,
+ resetOrigin: true,
+ },
+ {
+ description: "with null source",
+ message: () => createMessage(),
+ origin: trustedOrigin,
+ source: (): null => null,
+ },
+ {
+ description: "with unknown command",
+ message: () => createMessage({ command: "unknownCommand" }),
+ origin: trustedOrigin,
+ source: () => globalThis.parent,
+ },
+ ])("should reject messages $description", ({ message, origin, source, resetOrigin }) => {
+ if (resetOrigin) {
+ Object.defineProperty(globalThis, "location", {
+ value: { search: "" },
+ writable: true,
+ configurable: true,
+ });
+ }
+ const spy = jest.spyOn(globalThis.parent, "postMessage").mockImplementation();
+ postWindowMessage(message(), origin, source());
+ expect(spy).not.toHaveBeenCalled();
+ });
+
+ it("should accept and handle valid trusted messages", () => {
+ const spy = jest.spyOn(globalThis.parent, "postMessage").mockImplementation();
+ spy.mockClear();
+
+ const validMessage = createMessage({
+ parentOrigin: trustedOrigin,
+ initData: {
+ type: "change",
+ isVaultLocked: false,
+ removeIndividualVault: false,
+ importType: null,
+ launchTimestamp: Date.now(),
+ },
+ });
+ postWindowMessage(validMessage, trustedOrigin, globalThis.parent);
+ expect(validMessage.command).toBe("initNotificationBar");
+ expect(validMessage.parentOrigin).toBe(trustedOrigin);
+ expect(validMessage.initData).toBeDefined();
+ });
+});
diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts
index 3673a9f7321..333f8d5e534 100644
--- a/apps/browser/src/autofill/notification/bar.ts
+++ b/apps/browser/src/autofill/notification/bar.ts
@@ -24,6 +24,13 @@ import {
let notificationBarIframeInitData: NotificationBarIframeInitData = {};
let windowMessageOrigin: string;
+const urlParams = new URLSearchParams(globalThis.location.search);
+const trustedParentOrigin = urlParams.get("parentOrigin");
+
+if (trustedParentOrigin) {
+ windowMessageOrigin = trustedParentOrigin;
+}
+
const notificationBarWindowMessageHandlers: NotificationBarWindowMessageHandlers = {
initNotificationBar: ({ message }) => initNotificationBar(message),
saveCipherAttemptCompleted: ({ message }) => handleSaveCipherConfirmation(message),
@@ -395,15 +402,27 @@ function setupWindowMessageListener() {
}
function handleWindowMessage(event: MessageEvent) {
- if (!windowMessageOrigin) {
- windowMessageOrigin = event.origin;
- }
-
- if (event.origin !== windowMessageOrigin) {
+ if (event?.source !== globalThis.parent) {
return;
}
const message = event.data as NotificationBarWindowMessage;
+ if (!message?.command) {
+ return;
+ }
+
+ if (!windowMessageOrigin || event.origin !== windowMessageOrigin) {
+ return;
+ }
+
+ if (
+ message.command === "initNotificationBar" &&
+ message.parentOrigin &&
+ message.parentOrigin !== event.origin
+ ) {
+ return;
+ }
+
const handler = notificationBarWindowMessageHandlers[message.command];
if (!handler) {
return;
@@ -431,5 +450,8 @@ function getResolvedTheme(theme: Theme) {
}
function postMessageToParent(message: NotificationBarWindowMessage) {
- globalThis.parent.postMessage(message, windowMessageOrigin || "*");
+ if (!windowMessageOrigin) {
+ return;
+ }
+ globalThis.parent.postMessage(message, windowMessageOrigin);
}
diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-button.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-button.ts
index 642e7dd24e9..0836ecf5ff1 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-button.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-button.ts
@@ -10,6 +10,7 @@ export type InitAutofillInlineMenuButtonMessage = UpdateAuthStatusMessage & {
styleSheetUrl: string;
translations: Record;
portKey: string;
+ token: string;
};
export type AutofillInlineMenuButtonWindowMessageHandlers = {
diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts
index 64fa8dde124..98fd84373a8 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts
@@ -5,7 +5,7 @@ import { InlineMenuCipherData } from "../../../background/abstractions/overlay.b
export type AutofillInlineMenuContainerMessage = {
command: string;
portKey: string;
- token?: string;
+ token: string;
};
export type InitAutofillInlineMenuElementMessage = AutofillInlineMenuContainerMessage & {
diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts
index f5e1fe08850..cf778ef7892 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts
@@ -27,6 +27,7 @@ export type InitAutofillInlineMenuListMessage = AutofillInlineMenuListMessage &
showInlineMenuAccountCreation?: boolean;
showPasskeysLabels?: boolean;
portKey: string;
+ token: string;
generatedPassword?: string;
showSaveLoginMenu?: boolean;
};
diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts
index 9f2947c2e99..3bb86ee7876 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts
@@ -191,7 +191,7 @@ describe("AutofillInlineMenuIframeService", () => {
expect(
autofillInlineMenuIframeService["iframe"].contentWindow.postMessage,
- ).toHaveBeenCalledWith(message, "*");
+ ).toHaveBeenCalledWith(message, autofillInlineMenuIframeService["extensionOrigin"]);
});
it("handles port messages that are registered with the message handlers and does not pass the message on to the iframe", () => {
@@ -217,7 +217,7 @@ describe("AutofillInlineMenuIframeService", () => {
expect(autofillInlineMenuIframeService["portKey"]).toBe(portKey);
expect(
autofillInlineMenuIframeService["iframe"].contentWindow.postMessage,
- ).toHaveBeenCalledWith(message, "*");
+ ).toHaveBeenCalledWith(message, autofillInlineMenuIframeService["extensionOrigin"]);
});
});
@@ -242,7 +242,7 @@ describe("AutofillInlineMenuIframeService", () => {
expect(updateElementStylesSpy).not.toHaveBeenCalled();
expect(
autofillInlineMenuIframeService["iframe"].contentWindow.postMessage,
- ).toHaveBeenCalledWith(message, "*");
+ ).toHaveBeenCalledWith(message, autofillInlineMenuIframeService["extensionOrigin"]);
});
it("sets a light theme based on the user's system preferences", () => {
@@ -262,7 +262,7 @@ describe("AutofillInlineMenuIframeService", () => {
command: "initAutofillInlineMenuList",
theme: ThemeType.Light,
},
- "*",
+ autofillInlineMenuIframeService["extensionOrigin"],
);
});
@@ -283,7 +283,7 @@ describe("AutofillInlineMenuIframeService", () => {
command: "initAutofillInlineMenuList",
theme: ThemeType.Dark,
},
- "*",
+ autofillInlineMenuIframeService["extensionOrigin"],
);
});
@@ -387,7 +387,7 @@ describe("AutofillInlineMenuIframeService", () => {
command: "updateAutofillInlineMenuColorScheme",
colorScheme: "normal",
},
- "*",
+ autofillInlineMenuIframeService["extensionOrigin"],
);
});
diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts
index 9a9821f643c..8b1423b1290 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts
@@ -3,6 +3,7 @@
import { EVENTS } from "@bitwarden/common/autofill/constants";
import { ThemeTypes } from "@bitwarden/common/platform/enums";
+import { BrowserApi } from "../../../../platform/browser/browser-api";
import { sendExtensionMessage, setElementStyles } from "../../../utils";
import {
BackgroundPortMessageHandlers,
@@ -15,6 +16,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe
private readonly sendExtensionMessage = sendExtensionMessage;
private port: chrome.runtime.Port | null = null;
private portKey: string;
+ private readonly extensionOrigin: string;
private iframeMutationObserver: MutationObserver;
private iframe: HTMLIFrameElement;
private ariaAlertElement: HTMLDivElement;
@@ -69,6 +71,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe
private iframeTitle: string,
private ariaAlert?: string,
) {
+ this.extensionOrigin = BrowserApi.getRuntimeURL("")?.slice(0, -1);
this.iframeMutationObserver = new MutationObserver(this.handleMutations);
}
@@ -81,7 +84,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe
* that is declared.
*/
initMenuIframe() {
- this.defaultIframeAttributes.src = chrome.runtime.getURL("overlay/menu.html");
+ this.defaultIframeAttributes.src = BrowserApi.getRuntimeURL("overlay/menu.html");
this.defaultIframeAttributes.title = this.iframeTitle;
this.iframe = globalThis.document.createElement("iframe");
@@ -259,7 +262,10 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe
}
private postMessageToIFrame(message: any) {
- this.iframe.contentWindow?.postMessage({ portKey: this.portKey, ...message }, "*");
+ this.iframe.contentWindow?.postMessage(
+ { portKey: this.portKey, ...message },
+ this.extensionOrigin,
+ );
}
/**
diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts
index 7fa07850f00..10f6c905342 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts
@@ -1,5 +1,6 @@
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
+import { BrowserApi } from "../../../../../platform/browser/browser-api";
import { createInitAutofillInlineMenuButtonMessageMock } from "../../../../spec/autofill-mocks";
import { flushPromises, postWindowMessage } from "../../../../spec/testing-utils";
@@ -10,11 +11,11 @@ describe("AutofillInlineMenuButton", () => {
let autofillInlineMenuButton: AutofillInlineMenuButton;
const portKey: string = "inlineMenuButtonPortKey";
+ const expectedOrigin = BrowserApi.getRuntimeURL("")?.slice(0, -1) || "chrome-extension://id";
beforeEach(() => {
document.body.innerHTML = ``;
autofillInlineMenuButton = document.querySelector("autofill-inline-menu-button");
- autofillInlineMenuButton["messageOrigin"] = "https://localhost/";
jest.spyOn(globalThis.document, "createElement");
jest.spyOn(globalThis.parent, "postMessage");
});
@@ -56,8 +57,8 @@ describe("AutofillInlineMenuButton", () => {
autofillInlineMenuButton["buttonElement"].click();
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "autofillInlineMenuButtonClicked", portKey },
- "*",
+ { command: "autofillInlineMenuButtonClicked", portKey, token: "test-token" },
+ expectedOrigin,
);
});
});
@@ -70,7 +71,7 @@ describe("AutofillInlineMenuButton", () => {
it("does not post a message to close the autofill inline menu if the element is focused during the focus check", async () => {
jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true);
- postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused" });
+ postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused", token: "test-token" });
await flushPromises();
expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith({
@@ -84,7 +85,7 @@ describe("AutofillInlineMenuButton", () => {
.spyOn(autofillInlineMenuButton["buttonElement"], "querySelector")
.mockReturnValue(autofillInlineMenuButton["buttonElement"]);
- postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused" });
+ postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused", token: "test-token" });
await flushPromises();
expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith({
@@ -98,7 +99,7 @@ describe("AutofillInlineMenuButton", () => {
jest
.spyOn(autofillInlineMenuButton["buttonElement"], "querySelector")
.mockReturnValue(autofillInlineMenuButton["buttonElement"]);
- postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused" });
+ postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused", token: "test-token" });
await flushPromises();
globalThis.document.dispatchEvent(new MouseEvent("mouseout"));
@@ -113,12 +114,12 @@ describe("AutofillInlineMenuButton", () => {
jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(false);
jest.spyOn(autofillInlineMenuButton["buttonElement"], "querySelector").mockReturnValue(null);
- postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused" });
+ postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused", token: "test-token" });
await flushPromises();
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "triggerDelayedAutofillInlineMenuClosure", portKey },
- "*",
+ { command: "triggerDelayedAutofillInlineMenuClosure", portKey, token: "test-token" },
+ expectedOrigin,
);
});
@@ -128,6 +129,7 @@ describe("AutofillInlineMenuButton", () => {
postWindowMessage({
command: "updateAutofillInlineMenuButtonAuthStatus",
authStatus: AuthenticationStatus.Unlocked,
+ token: "test-token",
});
await flushPromises();
@@ -143,6 +145,7 @@ describe("AutofillInlineMenuButton", () => {
postWindowMessage({
command: "updateAutofillInlineMenuColorScheme",
colorScheme: "dark",
+ token: "test-token",
});
await flushPromises();
diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts
index b4e480797da..81bf7240230 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts
@@ -3,6 +3,7 @@ import { mock } from "jest-mock-extended";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CipherType } from "@bitwarden/common/vault/enums";
+import { BrowserApi } from "../../../../../platform/browser/browser-api";
import { InlineMenuCipherData } from "../../../../background/abstractions/overlay.background";
import {
createAutofillOverlayCipherDataMock,
@@ -23,6 +24,7 @@ describe("AutofillInlineMenuList", () => {
let autofillInlineMenuList: AutofillInlineMenuList | null;
const portKey: string = "inlineMenuListPortKey";
+ const expectedOrigin = BrowserApi.getRuntimeURL("")?.slice(0, -1) || "chrome-extension://id";
const events: { eventName: any; callback: any }[] = [];
beforeEach(() => {
@@ -67,8 +69,8 @@ describe("AutofillInlineMenuList", () => {
unlockButton.dispatchEvent(new Event("click"));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "unlockVault", portKey },
- "*",
+ { command: "unlockVault", portKey, token: "test-token" },
+ expectedOrigin,
);
});
});
@@ -134,8 +136,13 @@ describe("AutofillInlineMenuList", () => {
addVaultItemButton.dispatchEvent(new Event("click"));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "addNewVaultItem", portKey, addNewCipherType: CipherType.Login },
- "*",
+ {
+ command: "addNewVaultItem",
+ portKey,
+ addNewCipherType: CipherType.Login,
+ token: "test-token",
+ },
+ expectedOrigin,
);
});
});
@@ -324,8 +331,9 @@ describe("AutofillInlineMenuList", () => {
inlineMenuCipherId: "1",
usePasskey: false,
portKey,
+ token: "test-token",
},
- "*",
+ expectedOrigin,
);
});
@@ -492,8 +500,13 @@ describe("AutofillInlineMenuList", () => {
viewCipherButton.dispatchEvent(new Event("click"));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "viewSelectedCipher", inlineMenuCipherId: "1", portKey },
- "*",
+ {
+ command: "viewSelectedCipher",
+ inlineMenuCipherId: "1",
+ portKey,
+ token: "test-token",
+ },
+ expectedOrigin,
);
});
@@ -581,8 +594,13 @@ describe("AutofillInlineMenuList", () => {
newVaultItemButtonSpy.dispatchEvent(new Event("click"));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "addNewVaultItem", portKey, addNewCipherType: CipherType.Login },
- "*",
+ {
+ command: "addNewVaultItem",
+ portKey,
+ addNewCipherType: CipherType.Login,
+ token: "test-token",
+ },
+ expectedOrigin,
);
});
@@ -826,8 +844,8 @@ describe("AutofillInlineMenuList", () => {
fillGeneratedPasswordButton.dispatchEvent(new Event("click"));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "fillGeneratedPassword", portKey },
- "*",
+ { command: "fillGeneratedPassword", portKey, token: "test-token" },
+ expectedOrigin,
);
});
@@ -843,7 +861,7 @@ describe("AutofillInlineMenuList", () => {
expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith(
{ command: "fillGeneratedPassword", portKey },
- "*",
+ expectedOrigin,
);
});
@@ -857,8 +875,8 @@ describe("AutofillInlineMenuList", () => {
);
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "fillGeneratedPassword", portKey },
- "*",
+ { command: "fillGeneratedPassword", portKey, token: "test-token" },
+ expectedOrigin,
);
});
@@ -896,8 +914,8 @@ describe("AutofillInlineMenuList", () => {
refreshGeneratedPasswordButton.dispatchEvent(new Event("click"));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "refreshGeneratedPassword", portKey },
- "*",
+ { command: "refreshGeneratedPassword", portKey, token: "test-token" },
+ expectedOrigin,
);
});
@@ -913,7 +931,7 @@ describe("AutofillInlineMenuList", () => {
expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith(
{ command: "refreshGeneratedPassword", portKey },
- "*",
+ expectedOrigin,
);
});
@@ -927,8 +945,8 @@ describe("AutofillInlineMenuList", () => {
);
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "refreshGeneratedPassword", portKey },
- "*",
+ { command: "refreshGeneratedPassword", portKey, token: "test-token" },
+ expectedOrigin,
);
});
@@ -972,7 +990,7 @@ describe("AutofillInlineMenuList", () => {
it("does not post a `checkAutofillInlineMenuButtonFocused` message to the parent if the inline menu is currently focused", () => {
jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true);
- postWindowMessage({ command: "checkAutofillInlineMenuListFocused" });
+ postWindowMessage({ command: "checkAutofillInlineMenuListFocused", token: "test-token" });
expect(globalThis.parent.postMessage).not.toHaveBeenCalled();
});
@@ -983,7 +1001,7 @@ describe("AutofillInlineMenuList", () => {
.spyOn(autofillInlineMenuList["inlineMenuListContainer"], "querySelector")
.mockReturnValue(autofillInlineMenuList["inlineMenuListContainer"]);
- postWindowMessage({ command: "checkAutofillInlineMenuListFocused" });
+ postWindowMessage({ command: "checkAutofillInlineMenuListFocused", token: "test-token" });
expect(globalThis.parent.postMessage).not.toHaveBeenCalled();
});
@@ -994,7 +1012,7 @@ describe("AutofillInlineMenuList", () => {
jest
.spyOn(autofillInlineMenuList["inlineMenuListContainer"], "querySelector")
.mockReturnValue(autofillInlineMenuList["inlineMenuListContainer"]);
- postWindowMessage({ command: "checkAutofillInlineMenuListFocused" });
+ postWindowMessage({ command: "checkAutofillInlineMenuListFocused", token: "test-token" });
await flushPromises();
globalThis.document.dispatchEvent(new MouseEvent("mouseout"));
@@ -1010,11 +1028,11 @@ describe("AutofillInlineMenuList", () => {
.spyOn(autofillInlineMenuList["inlineMenuListContainer"], "querySelector")
.mockReturnValue(null);
- postWindowMessage({ command: "checkAutofillInlineMenuListFocused" });
+ postWindowMessage({ command: "checkAutofillInlineMenuListFocused", token: "test-token" });
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "checkAutofillInlineMenuButtonFocused", portKey },
- "*",
+ { command: "checkAutofillInlineMenuButtonFocused", portKey, token: "test-token" },
+ expectedOrigin,
);
});
@@ -1022,7 +1040,7 @@ describe("AutofillInlineMenuList", () => {
postWindowMessage(createInitAutofillInlineMenuListMessageMock());
const updateCiphersSpy = jest.spyOn(autofillInlineMenuList as any, "updateListItems");
- postWindowMessage({ command: "updateAutofillInlineMenuListCiphers" });
+ postWindowMessage({ command: "updateAutofillInlineMenuListCiphers", token: "test-token" });
expect(updateCiphersSpy).toHaveBeenCalled();
});
@@ -1062,7 +1080,10 @@ describe("AutofillInlineMenuList", () => {
postWindowMessage(createInitAutofillInlineMenuListMessageMock());
await flushPromises();
- postWindowMessage({ command: "updateAutofillInlineMenuGeneratedPassword" });
+ postWindowMessage({
+ command: "updateAutofillInlineMenuGeneratedPassword",
+ token: "test-token",
+ });
expect(buildColorizedPasswordElementSpy).not.toHaveBeenCalled();
});
@@ -1074,6 +1095,7 @@ describe("AutofillInlineMenuList", () => {
postWindowMessage({
command: "updateAutofillInlineMenuGeneratedPassword",
generatedPassword,
+ token: "test-token",
});
expect(buildPasswordGeneratorSpy).toHaveBeenCalled();
@@ -1090,6 +1112,7 @@ describe("AutofillInlineMenuList", () => {
postWindowMessage({
command: "updateAutofillInlineMenuGeneratedPassword",
generatedPassword,
+ token: "test-token",
});
expect(buildPasswordGeneratorSpy).toHaveBeenCalledTimes(1);
@@ -1115,7 +1138,7 @@ describe("AutofillInlineMenuList", () => {
);
await flushPromises();
- postWindowMessage({ command: "showSaveLoginInlineMenuList" });
+ postWindowMessage({ command: "showSaveLoginInlineMenuList", token: "test-token" });
expect(buildSaveLoginInlineMenuSpy).not.toHaveBeenCalled();
});
@@ -1124,7 +1147,7 @@ describe("AutofillInlineMenuList", () => {
postWindowMessage(createInitAutofillInlineMenuListMessageMock());
await flushPromises();
- postWindowMessage({ command: "showSaveLoginInlineMenuList" });
+ postWindowMessage({ command: "showSaveLoginInlineMenuList", token: "test-token" });
expect(buildSaveLoginInlineMenuSpy).toHaveBeenCalled();
});
@@ -1143,7 +1166,7 @@ describe("AutofillInlineMenuList", () => {
"setAttribute",
);
- postWindowMessage({ command: "focusAutofillInlineMenuList" });
+ postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" });
expect(inlineMenuContainerSetAttributeSpy).toHaveBeenCalledWith("role", "dialog");
expect(inlineMenuContainerSetAttributeSpy).toHaveBeenCalledWith("aria-modal", "true");
@@ -1161,7 +1184,7 @@ describe("AutofillInlineMenuList", () => {
autofillInlineMenuList["inlineMenuListContainer"].querySelector("#unlock-button");
jest.spyOn(unlockButton as HTMLElement, "focus");
- postWindowMessage({ command: "focusAutofillInlineMenuList" });
+ postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" });
expect((unlockButton as HTMLElement).focus).toBeCalled();
});
@@ -1173,7 +1196,7 @@ describe("AutofillInlineMenuList", () => {
autofillInlineMenuList["inlineMenuListContainer"].querySelector("#new-item-button");
jest.spyOn(newItemButton as HTMLElement, "focus");
- postWindowMessage({ command: "focusAutofillInlineMenuList" });
+ postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" });
expect((newItemButton as HTMLElement).focus).toBeCalled();
});
@@ -1184,7 +1207,7 @@ describe("AutofillInlineMenuList", () => {
autofillInlineMenuList["inlineMenuListContainer"].querySelector(".fill-cipher-button");
jest.spyOn(firstCipherItem as HTMLElement, "focus");
- postWindowMessage({ command: "focusAutofillInlineMenuList" });
+ postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" });
expect((firstCipherItem as HTMLElement).focus).toBeCalled();
});
@@ -1197,8 +1220,8 @@ describe("AutofillInlineMenuList", () => {
globalThis.dispatchEvent(new Event("blur"));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "autofillInlineMenuBlurred", portKey },
- "*",
+ { command: "autofillInlineMenuBlurred", portKey, token: "test-token" },
+ expectedOrigin,
);
});
});
@@ -1220,8 +1243,13 @@ describe("AutofillInlineMenuList", () => {
);
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "redirectAutofillInlineMenuFocusOut", direction: "previous", portKey },
- "*",
+ {
+ command: "redirectAutofillInlineMenuFocusOut",
+ direction: "previous",
+ portKey,
+ token: "test-token",
+ },
+ expectedOrigin,
);
});
@@ -1229,8 +1257,13 @@ describe("AutofillInlineMenuList", () => {
globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "Tab" }));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "redirectAutofillInlineMenuFocusOut", direction: "next", portKey },
- "*",
+ {
+ command: "redirectAutofillInlineMenuFocusOut",
+ direction: "next",
+ portKey,
+ token: "test-token",
+ },
+ expectedOrigin,
);
});
@@ -1238,8 +1271,13 @@ describe("AutofillInlineMenuList", () => {
globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "Escape" }));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "redirectAutofillInlineMenuFocusOut", direction: "current", portKey },
- "*",
+ {
+ command: "redirectAutofillInlineMenuFocusOut",
+ direction: "current",
+ portKey,
+ token: "test-token",
+ },
+ expectedOrigin,
);
});
});
@@ -1274,8 +1312,13 @@ describe("AutofillInlineMenuList", () => {
autofillInlineMenuList["handleResizeObserver"](entries as unknown as ResizeObserverEntry[]);
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
- { command: "updateAutofillInlineMenuListHeight", styles: { height: "300px" }, portKey },
- "*",
+ {
+ command: "updateAutofillInlineMenuListHeight",
+ styles: { height: "300px" },
+ portKey,
+ token: "test-token",
+ },
+ expectedOrigin,
);
});
});
diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts
index aea6ef30b99..6c61cfae6b4 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts
@@ -1,5 +1,6 @@
import { EVENTS } from "@bitwarden/common/autofill/constants";
+import { BrowserApi } from "../../../../../platform/browser/browser-api";
import { generateRandomChars, setElementStyles } from "../../../../utils";
import {
InitAutofillInlineMenuElementMessage,
@@ -73,7 +74,7 @@ export class AutofillInlineMenuContainer {
constructor() {
this.token = generateRandomChars(32);
- this.extensionOrigin = chrome.runtime.getURL("").slice(0, -1);
+ this.extensionOrigin = BrowserApi.getRuntimeURL("")?.slice(0, -1);
globalThis.addEventListener("message", this.handleWindowMessage);
}
@@ -203,6 +204,9 @@ export class AutofillInlineMenuContainer {
*/
private handleWindowMessage = (event: MessageEvent) => {
const message = event.data;
+ if (!message?.command) {
+ return;
+ }
if (this.isForeignWindowMessage(event)) {
return;
}
@@ -287,7 +291,10 @@ export class AutofillInlineMenuContainer {
* every time the inline menu container is recreated.
*
*/
- private isValidSessionToken(message: { token?: string }): boolean {
+ private isValidSessionToken(message: { token: string }): boolean {
+ if (!this.token || !message?.token || !message?.token.length) {
+ return false;
+ }
return message.token === this.token;
}
diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts
index ea77e3e434d..5df6e7cd190 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts
@@ -38,12 +38,8 @@ export class AutofillInlineMenuPageElement extends HTMLElement {
styleSheetUrl: string,
translations: Record,
portKey: string,
- token?: string,
): Promise {
this.portKey = portKey;
- if (token) {
- this.token = token;
- }
this.translations = translations;
globalThis.document.documentElement.setAttribute("lang", this.getTranslation("locale"));
@@ -63,11 +59,16 @@ export class AutofillInlineMenuPageElement extends HTMLElement {
* @param message - The message to post
*/
protected postMessageToParent(message: AutofillInlineMenuPageElementWindowMessage) {
- const messageWithAuth: Record = { portKey: this.portKey, ...message };
- if (this.token) {
- messageWithAuth.token = this.token;
+ // never send messages containing authentication tokens without a valid token and an established messageOrigin
+ if (!this.token || !this.messageOrigin) {
+ return;
}
- globalThis.parent.postMessage(messageWithAuth, "*");
+ const messageWithAuth: Record = {
+ portKey: this.portKey,
+ ...message,
+ token: this.token,
+ };
+ globalThis.parent.postMessage(messageWithAuth, this.messageOrigin);
}
/**
@@ -105,6 +106,10 @@ export class AutofillInlineMenuPageElement extends HTMLElement {
return;
}
+ if (event.source !== globalThis.parent) {
+ return;
+ }
+
if (!this.messageOrigin) {
this.messageOrigin = event.origin;
}
@@ -115,12 +120,23 @@ export class AutofillInlineMenuPageElement extends HTMLElement {
const message = event?.data;
- if (
- message?.token &&
- (message?.command === "initAutofillInlineMenuButton" ||
- message?.command === "initAutofillInlineMenuList")
- ) {
+ if (!message?.command) {
+ return;
+ }
+
+ const isInitCommand =
+ message.command === "initAutofillInlineMenuButton" ||
+ message.command === "initAutofillInlineMenuList";
+
+ if (isInitCommand) {
+ if (!message?.token) {
+ return;
+ }
this.token = message.token;
+ } else {
+ if (!this.token || !message?.token || message.token !== this.token) {
+ return;
+ }
}
const handler = this.windowMessageHandlers[message?.command];
diff --git a/apps/browser/src/autofill/overlay/notifications/content/__snapshots__/overlay-notifications-content.service.spec.ts.snap b/apps/browser/src/autofill/overlay/notifications/content/__snapshots__/overlay-notifications-content.service.spec.ts.snap
index 39ca68d912c..cfcedc9da7a 100644
--- a/apps/browser/src/autofill/overlay/notifications/content/__snapshots__/overlay-notifications-content.service.spec.ts.snap
+++ b/apps/browser/src/autofill/overlay/notifications/content/__snapshots__/overlay-notifications-content.service.spec.ts.snap
@@ -7,7 +7,7 @@ exports[`OverlayNotificationsContentService opening the notification bar creates
>
diff --git a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.spec.ts b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.spec.ts
index d5e8c559326..b016a91d8a4 100644
--- a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.spec.ts
+++ b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.spec.ts
@@ -155,8 +155,9 @@ describe("OverlayNotificationsContentService", () => {
{
command: "initNotificationBar",
initData: expect.any(Object),
+ parentOrigin: expect.any(String),
},
- "*",
+ overlayNotificationsContentService["extensionOrigin"],
);
});
});
@@ -257,7 +258,7 @@ describe("OverlayNotificationsContentService", () => {
expect(postMessageSpy).toHaveBeenCalledWith(
{ command: "saveCipherAttemptCompleted", error: undefined },
- "*",
+ overlayNotificationsContentService["extensionOrigin"],
);
});
});
diff --git a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts
index 0afa4f1409b..8dc08169468 100644
--- a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts
+++ b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts
@@ -2,6 +2,7 @@
// @ts-strict-ignore
import { EVENTS } from "@bitwarden/common/autofill/constants";
+import { BrowserApi } from "../../../../platform/browser/browser-api";
import {
NotificationBarIframeInitData,
NotificationType,
@@ -22,6 +23,7 @@ export class OverlayNotificationsContentService
private notificationBarIframeElement: HTMLIFrameElement | null = null;
private notificationBarShadowRoot: ShadowRoot | null = null;
private currentNotificationBarType: NotificationType | null = null;
+ private readonly extensionOrigin: string;
private notificationBarContainerStyles: Partial