diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index 2ca0920a38..b2ad7128b6 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -1299,7 +1299,11 @@ describe("OverlayBackground", () => { beforeEach(() => { jest.useFakeTimers(); - sender = mock({ tab: { id: 1 } }); + sender = mock({ + tab: { id: 1 }, + url: "https://top-frame-test.com", + frameId: 0, + }); openAddEditVaultItemPopoutSpy = jest .spyOn(overlayBackground as any, "openAddEditVaultItemPopout") .mockImplementation(); @@ -1482,10 +1486,15 @@ describe("OverlayBackground", () => { describe("pulling cipher data from multiple frames of a tab", () => { let subFrameSender: MockProxy; + let secondSubFrameSender: MockProxy; const command = "autofillOverlayAddNewVaultItem"; beforeEach(() => { subFrameSender = mock({ tab: sender.tab, frameId: 2 }); + secondSubFrameSender = mock({ + tab: sender.tab, + frameId: 3, + }); }); it("combines the login cipher data from all frames", async () => { @@ -1494,9 +1503,15 @@ describe("OverlayBackground", () => { "buildLoginCipherView", ); const addNewCipherType = CipherType.Login; + const topLevelLoginCipherData = { + uri: "https://top-frame-test.com", + hostname: "top-frame-test.com", + username: "", + password: "", + }; const loginCipherData = { uri: "https://tacos.com", - hostname: "", + hostname: "tacos.com", username: "username", password: "", }; @@ -1507,11 +1522,56 @@ describe("OverlayBackground", () => { password: "password", }; - sendMockExtensionMessage({ command, addNewCipherType, login: loginCipherData }, sender); + sendMockExtensionMessage( + { command, addNewCipherType, login: topLevelLoginCipherData }, + sender, + ); + sendMockExtensionMessage( + { command, addNewCipherType, login: loginCipherData }, + subFrameSender, + ); sendMockExtensionMessage( { command, addNewCipherType, login: subFrameLoginCipherData }, + secondSubFrameSender, + ); + jest.advanceTimersByTime(100); + await flushPromises(); + + expect(buildLoginCipherViewSpy).toHaveBeenCalledWith({ + uri: "https://top-frame-test.com", + hostname: "top-frame-test.com", + username: "username", + password: "password", + }); + }); + + it("sets the uri to the subframe of a tab if the login data is complete", async () => { + const buildLoginCipherViewSpy = jest.spyOn( + overlayBackground as any, + "buildLoginCipherView", + ); + const addNewCipherType = CipherType.Login; + const loginCipherData = { + uri: "https://tacos.com", + hostname: "tacos.com", + username: "username", + password: "password", + }; + const topLevelLoginCipherData = { + uri: "https://top-frame-test.com", + hostname: "top-frame-test.com", + username: "", + password: "", + }; + + sendMockExtensionMessage( + { command, addNewCipherType, login: loginCipherData }, subFrameSender, ); + sendMockExtensionMessage( + { command, addNewCipherType, login: topLevelLoginCipherData }, + sender, + ); jest.advanceTimersByTime(100); await flushPromises(); diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 2cef0b0e78..6e7db21a6d 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -1589,7 +1589,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { } if (login && this.isAddingNewLogin()) { - this.updateCurrentAddNewItemLogin(login); + this.updateCurrentAddNewItemLogin(login, sender); } if (card && this.isAddingNewCard()) { @@ -1629,22 +1629,56 @@ export class OverlayBackground implements OverlayBackgroundInterface { * login data is already present, the data will be merged with the existing data. * * @param login - The login data captured from the extension message + * @param sender - The sender of the extension message */ - private updateCurrentAddNewItemLogin(login: NewLoginCipherData) { + private updateCurrentAddNewItemLogin( + login: NewLoginCipherData, + sender: chrome.runtime.MessageSender, + ) { + const { username, password } = login; + + if (this.partialLoginDataFoundInSubFrame(sender, login)) { + login.uri = ""; + login.hostname = ""; + } + if (!this.currentAddNewItemData.login) { this.currentAddNewItemData.login = login; return; } const currentLoginData = this.currentAddNewItemData.login; + if (sender.frameId === 0 && currentLoginData.hostname && !username && !password) { + login.uri = ""; + login.hostname = ""; + } + this.currentAddNewItemData.login = { uri: login.uri || currentLoginData.uri, hostname: login.hostname || currentLoginData.hostname, - username: login.username || currentLoginData.username, - password: login.password || currentLoginData.password, + username: username || currentLoginData.username, + password: password || currentLoginData.password, }; } + /** + * Handles verifying if the login data for a tab is separated between various + * iframe elements. If that is the case, we want to ignore the login uri and + * domain to ensure the top frame is treated as the primary source of login data. + * + * @param sender - The sender of the extension message + * @param login - The login data captured from the extension message + */ + private partialLoginDataFoundInSubFrame( + sender: chrome.runtime.MessageSender, + login: NewLoginCipherData, + ) { + const { frameId } = sender; + const { username, password } = login; + + return frameId !== 0 && (!username || !password); + } + /** * Updates the current add new item data with the provided card data. If the * card data is already present, the data will be merged with the existing data.