1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-11 10:10:25 +01:00

[PM-11474] Rework injection of delayed passkey script to address identified bugs (#10830)

* [PM-11474] Delay cleanup of injected passkey script

* [PM-11474] Incorporating changes to ensure passkeys function within Safari script effectively

* [PM-11474] Fixing jest tests

* [PM-11474] Fixing jest tests
This commit is contained in:
Cesar Gonzalez 2024-09-04 15:42:37 -05:00 committed by GitHub
parent 55ffe9913d
commit e594059865
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 41 additions and 24 deletions

View File

@ -4,12 +4,21 @@ describe("FIDO2 page-script for manifest v2", () => {
let createdScriptElement: HTMLScriptElement; let createdScriptElement: HTMLScriptElement;
jest.spyOn(window.document, "createElement"); jest.spyOn(window.document, "createElement");
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => { afterEach(() => {
Object.defineProperty(window.document, "contentType", { value: "text/html", writable: true }); Object.defineProperty(window.document, "contentType", { value: "text/html", writable: true });
jest.clearAllMocks(); jest.clearAllMocks();
jest.clearAllTimers();
jest.resetModules(); jest.resetModules();
}); });
afterAll(() => {
jest.useRealTimers();
});
it("skips appending the `page-script.js` file if the document contentType is not `text/html`", () => { it("skips appending the `page-script.js` file if the document contentType is not `text/html`", () => {
Object.defineProperty(window.document, "contentType", { value: "text/plain", writable: true }); Object.defineProperty(window.document, "contentType", { value: "text/plain", writable: true });
@ -19,7 +28,7 @@ describe("FIDO2 page-script for manifest v2", () => {
}); });
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`", () => {
jest.spyOn(window.document.head, "insertBefore").mockImplementation((node) => { jest.spyOn(window.document.head, "prepend").mockImplementation((node) => {
createdScriptElement = node as HTMLScriptElement; createdScriptElement = node as HTMLScriptElement;
return node; return node;
}); });
@ -28,16 +37,13 @@ describe("FIDO2 page-script for manifest v2", () => {
expect(window.document.createElement).toHaveBeenCalledWith("script"); expect(window.document.createElement).toHaveBeenCalledWith("script");
expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript); expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript);
expect(window.document.head.insertBefore).toHaveBeenCalledWith( expect(window.document.head.prepend).toHaveBeenCalledWith(expect.any(HTMLScriptElement));
expect.any(HTMLScriptElement),
window.document.head.firstChild,
);
expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`); expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`);
}); });
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", () => {
window.document.documentElement.removeChild(window.document.head); window.document.documentElement.removeChild(window.document.head);
jest.spyOn(window.document.documentElement, "insertBefore").mockImplementation((node) => { jest.spyOn(window.document.documentElement, "prepend").mockImplementation((node) => {
createdScriptElement = node as HTMLScriptElement; createdScriptElement = node as HTMLScriptElement;
return node; return node;
}); });
@ -46,9 +52,8 @@ describe("FIDO2 page-script for manifest v2", () => {
expect(window.document.createElement).toHaveBeenCalledWith("script"); expect(window.document.createElement).toHaveBeenCalledWith("script");
expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript); expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript);
expect(window.document.documentElement.insertBefore).toHaveBeenCalledWith( expect(window.document.documentElement.prepend).toHaveBeenCalledWith(
expect.any(HTMLScriptElement), expect.any(HTMLScriptElement),
window.document.documentElement.firstChild,
); );
expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`); expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`);
}); });
@ -63,6 +68,7 @@ describe("FIDO2 page-script for manifest v2", () => {
jest.spyOn(createdScriptElement, "remove"); jest.spyOn(createdScriptElement, "remove");
createdScriptElement.dispatchEvent(new Event("load")); createdScriptElement.dispatchEvent(new Event("load"));
jest.runAllTimers();
expect(createdScriptElement.remove).toHaveBeenCalled(); expect(createdScriptElement.remove).toHaveBeenCalled();
}); });

View File

@ -2,18 +2,20 @@
* This script handles injection of the FIDO2 override page script into the document. * 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. * This is required for manifest v2, but will be removed when we migrate fully to manifest v3.
*/ */
import { Fido2ContentScript } from "../enums/fido2-content-script.enum";
(function (globalContext) { (function (globalContext) {
if (globalContext.document.contentType !== "text/html") { if (globalContext.document.contentType !== "text/html") {
return; return;
} }
const script = globalContext.document.createElement("script"); const script = globalContext.document.createElement("script");
script.src = chrome.runtime.getURL(Fido2ContentScript.PageScript); script.src = chrome.runtime.getURL("content/fido2-page-script.js");
script.addEventListener("load", () => script.remove()); script.addEventListener("load", removeScriptOnLoad);
const scriptInsertionPoint = const scriptInsertionPoint =
globalContext.document.head || globalContext.document.documentElement; globalContext.document.head || globalContext.document.documentElement;
scriptInsertionPoint.insertBefore(script, scriptInsertionPoint.firstChild); scriptInsertionPoint.prepend(script);
function removeScriptOnLoad() {
globalThis.setTimeout(() => script?.remove(), 5000);
}
})(globalThis); })(globalThis);

View File

@ -2,26 +2,35 @@
* This script handles injection of the FIDO2 override page script into the document. * 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. * This is required for manifest v2, but will be removed when we migrate fully to manifest v3.
*/ */
import { Fido2ContentScript } from "../enums/fido2-content-script.enum";
(function (globalContext) { (function (globalContext) {
if (globalContext.document.contentType !== "text/html") { if (globalContext.document.contentType !== "text/html") {
return; return;
} }
if (globalContext.document.readyState === "complete") { const script = globalContext.document.createElement("script");
loadScript(); script.src = chrome.runtime.getURL("content/fido2-page-script.js");
script.addEventListener("load", removeScriptOnLoad);
// 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.
// @see https://github.com/bitwarden/clients/issues/9618
const delayScriptInjection =
globalContext.window.top !== globalContext.window &&
globalContext.document.readyState !== "complete";
if (delayScriptInjection) {
globalContext.document.addEventListener("DOMContentLoaded", injectScript);
} else { } else {
globalContext.addEventListener("DOMContentLoaded", loadScript); injectScript();
} }
function loadScript() { function injectScript() {
const script = globalContext.document.createElement("script");
script.src = chrome.runtime.getURL(Fido2ContentScript.PageScript);
script.addEventListener("load", () => script.remove());
const scriptInsertionPoint = const scriptInsertionPoint =
globalContext.document.head || globalContext.document.documentElement; globalContext.document.head || globalContext.document.documentElement;
scriptInsertionPoint.insertBefore(script, scriptInsertionPoint.firstChild); scriptInsertionPoint.prepend(script);
}
function removeScriptOnLoad() {
globalThis.setTimeout(() => script?.remove(), 5000);
} }
})(globalThis); })(globalThis);