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

[PM-10897] Fixing invalid url reference when creating login ciphers from inline menu (#10527)

* [PM-10897] Fixing invalid url reference when creating login ciphers from inline menu

* [PM-10897] Ensuring that a subframe that contains a full set of data for a login cipher is treated as authoritative
This commit is contained in:
Cesar Gonzalez 2024-08-15 08:45:55 -05:00 committed by GitHub
parent 1cf2c14e82
commit 0b24789449
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 101 additions and 7 deletions

View File

@ -1299,7 +1299,11 @@ describe("OverlayBackground", () => {
beforeEach(() => {
jest.useFakeTimers();
sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 } });
sender = mock<chrome.runtime.MessageSender>({
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<chrome.runtime.MessageSender>;
let secondSubFrameSender: MockProxy<chrome.runtime.MessageSender>;
const command = "autofillOverlayAddNewVaultItem";
beforeEach(() => {
subFrameSender = mock<chrome.runtime.MessageSender>({ tab: sender.tab, frameId: 2 });
secondSubFrameSender = mock<chrome.runtime.MessageSender>({
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();

View File

@ -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.