From 27509bc6f9078bdbb6c8c389fa13b683f90d0e19 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 1 Dec 2025 08:48:16 +0100 Subject: [PATCH] [PM-28640] Fix passkeys not working on MV2 (#17701) * fix: inject script contents directly * fix: tests * fix: tests * fix: injection tests (cherry picked from commit 2fd4a92cc57e83442bbbe64629a9a02ee775d7f9) --- .../fido2/background/fido2.background.spec.ts | 2 +- .../fido2/background/fido2.background.ts | 2 +- .../fido2-page-script-append.mv2.spec.ts | 18 ++++++++++++++---- .../fido2-page-script-delay-append.mv2.ts | 15 +++++++++++++-- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts index adb59b8f845..76ad78a6cd8 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts @@ -203,7 +203,7 @@ describe("Fido2Background", () => { { file: Fido2ContentScript.PageScriptDelayAppend }, { file: Fido2ContentScript.ContentScript }, ], - world: "MAIN", + world: "ISOLATED", ...sharedRegistrationOptions, }); }); diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.ts b/apps/browser/src/autofill/fido2/background/fido2.background.ts index a8b016a14d6..0ee7a43767f 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.ts @@ -176,7 +176,7 @@ export class Fido2Background implements Fido2BackgroundInterface { { file: await this.getFido2PageScriptAppendFileName() }, { file: Fido2ContentScript.ContentScript }, ], - world: "MAIN", + world: "ISOLATED", ...this.sharedRegistrationOptions, }); } diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script-append.mv2.spec.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script-append.mv2.spec.ts index b444c967080..0b10841e390 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script-append.mv2.spec.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script-append.mv2.spec.ts @@ -29,38 +29,48 @@ describe("FIDO2 page-script for manifest v2", () => { expect(window.document.createElement).not.toHaveBeenCalled(); }); - it("appends the `page-script.js` file to the document head when the contentType is `text/html`", () => { + it("appends the `page-script.js` file to the document head when the contentType is `text/html`", async () => { + const scriptContents = "test-script-contents"; jest.spyOn(window.document.head, "prepend").mockImplementation((node) => { createdScriptElement = node as HTMLScriptElement; return node; }); + window.fetch = jest.fn().mockResolvedValue({ + text: () => Promise.resolve(scriptContents), + } as Response); // FIXME: Remove when updating file. Eslint update // eslint-disable-next-line @typescript-eslint/no-require-imports require("./fido2-page-script-delay-append.mv2.ts"); + await jest.runAllTimersAsync(); expect(window.document.createElement).toHaveBeenCalledWith("script"); expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript); expect(window.document.head.prepend).toHaveBeenCalledWith(expect.any(HTMLScriptElement)); - expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`); + expect(createdScriptElement.innerHTML).toBe(scriptContents); }); - it("appends the `page-script.js` file to the document element if the head is not available", () => { + it("appends the `page-script.js` file to the document element if the head is not available", async () => { + const scriptContents = "test-script-contents"; window.document.documentElement.removeChild(window.document.head); jest.spyOn(window.document.documentElement, "prepend").mockImplementation((node) => { createdScriptElement = node as HTMLScriptElement; return node; }); + window.fetch = jest.fn().mockResolvedValue({ + text: () => Promise.resolve(scriptContents), + } as Response); // FIXME: Remove when updating file. Eslint update // eslint-disable-next-line @typescript-eslint/no-require-imports require("./fido2-page-script-delay-append.mv2.ts"); + await jest.runAllTimersAsync(); expect(window.document.createElement).toHaveBeenCalledWith("script"); expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript); expect(window.document.documentElement.prepend).toHaveBeenCalledWith( expect.any(HTMLScriptElement), ); - expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`); + expect(createdScriptElement.innerHTML).toBe(scriptContents); }); }); diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts index 775bc76266d..8c0d17c7e21 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts @@ -2,15 +2,26 @@ * This script handles injection of the FIDO2 override page script into the document. * This is required for manifest v2, but will be removed when we migrate fully to manifest v3. */ -(function (globalContext) { +void (async function (globalContext) { if (globalContext.document.contentType !== "text/html") { return; } const script = globalContext.document.createElement("script"); - script.src = chrome.runtime.getURL("content/fido2-page-script.js"); script.async = false; + const pageScriptUrl = chrome.runtime.getURL("content/fido2-page-script.js"); + // Inject the script contents directly to avoid leaking the extension URL + try { + const response = await fetch(pageScriptUrl); + const scriptContents = await response.text(); + script.innerHTML = scriptContents; + } catch { + // eslint-disable-next-line no-console + console.error("Failed to load FIDO2 page script contents. Injection failed."); + return; + } + // We are ensuring that the script injection is delayed in the event that we are loading // within an iframe element. This prevents an issue with web mail clients that load content // using ajax within iframes. In particular, Zimbra web mail client was observed to have this issue.