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

[PM-11481] Incorporate TOTP field qualification to ensure inline menu appears correctly for login forms (#10900)

* [PM-11481] Incorporate TOTP field qualification to ensure inline menu appears correctly for login forms

* [PM-11481] Incorporate TOTP field qualification to ensure inline menu appears correctly for login forms
This commit is contained in:
Cesar Gonzalez 2024-09-06 16:00:47 -05:00 committed by GitHub
parent 12b5ce548d
commit 4e1912e24e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 57 additions and 7 deletions

View File

@ -13,6 +13,7 @@ export type SubmitButtonKeywordsMap = WeakMap<HTMLElement, string>;
export interface InlineMenuFieldQualificationService {
isUsernameField(field: AutofillField): boolean;
isCurrentPasswordField(field: AutofillField): boolean;
isNewPasswordField(field: AutofillField): boolean;
isEmailField(field: AutofillField): boolean;
isFieldForLoginForm(field: AutofillField, pageDetails: AutofillPageDetails): boolean;

View File

@ -87,6 +87,11 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
destroyAutofillInlineMenuListeners: () => this.destroy(),
getFormFieldDataForNotification: () => this.handleGetFormFieldDataForNotificationMessage(),
};
private readonly loginFieldQualifiers: Record<string, CallableFunction> = {
[AutofillFieldQualifier.username]: this.inlineMenuFieldQualificationService.isUsernameField,
[AutofillFieldQualifier.password]:
this.inlineMenuFieldQualificationService.isCurrentPasswordField,
};
private readonly cardFieldQualifiers: Record<string, CallableFunction> = {
[AutofillFieldQualifier.cardholderName]:
this.inlineMenuFieldQualificationService.isFieldForCardholderName,
@ -781,12 +786,14 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
* @param autofillFieldData - Autofill field data captured from the form field element.
*/
private qualifyUserFilledLoginField(autofillFieldData: AutofillField) {
if (autofillFieldData.type === "password") {
autofillFieldData.fieldQualifier = AutofillFieldQualifier.password;
for (const [fieldQualifier, fieldQualifierFunction] of Object.entries(
this.loginFieldQualifiers,
)) {
if (fieldQualifierFunction(autofillFieldData)) {
autofillFieldData.fieldQualifier = fieldQualifier as AutofillFieldQualifierType;
return;
}
autofillFieldData.fieldQualifier = AutofillFieldQualifier.username;
}
}
/**

View File

@ -21,12 +21,29 @@ describe("InlineMenuFieldQualificationService", () => {
});
describe("isFieldForLoginForm", () => {
it("disqualifies totp fields", () => {
const field = mock<AutofillField>({
type: "text",
autoCompleteType: "one-time-code",
htmlName: "totp",
htmlID: "totp",
placeholder: "totp",
});
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(
false,
);
});
describe("qualifying a password field for a login form", () => {
describe("an invalid password field", () => {
it("has a `new-password` autoCompleteType", () => {
const field = mock<AutofillField>({
type: "password",
autoCompleteType: "new-password",
htmlName: "input-password",
htmlID: "input-password",
placeholder: "input-password",
});
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(
@ -39,6 +56,8 @@ describe("InlineMenuFieldQualificationService", () => {
type: "password",
placeholder: "create account password",
autoCompleteType: "",
htmlName: "input-password",
htmlID: "input-password",
});
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(

View File

@ -125,6 +125,7 @@ export class InlineMenuFieldQualificationService
this.identityCompanyAutocompleteValue,
this.identityPostalCodeAutocompleteValue,
]);
private totpFieldAutocompleteValue = "one-time-code";
private inlineMenuFieldQualificationFlagSet = false;
constructor() {
@ -145,6 +146,11 @@ export class InlineMenuFieldQualificationService
return this.isFieldForLoginFormFallback(field);
}
const isTotpField = this.isTotpField(field);
if (isTotpField) {
return false;
}
const isCurrentPasswordField = this.isCurrentPasswordField(field);
if (isCurrentPasswordField) {
return this.isPasswordFieldForLoginForm(field, pageDetails);
@ -838,7 +844,7 @@ export class InlineMenuFieldQualificationService
*
* @param field - The field to validate
*/
private isCurrentPasswordField = (field: AutofillField): boolean => {
isCurrentPasswordField = (field: AutofillField): boolean => {
if (
this.fieldContainsAutocompleteValues(field, this.newPasswordAutoCompleteValue) ||
this.keywordsFoundInFieldData(field, this.accountCreationFieldKeywords)
@ -875,7 +881,8 @@ export class InlineMenuFieldQualificationService
if (
(!isInputPasswordType &&
this.isExcludedFieldType(field, this.excludedAutofillFieldTypesSet)) ||
this.fieldHasDisqualifyingAttributeValue(field)
this.fieldHasDisqualifyingAttributeValue(field) ||
this.isTotpField(field)
) {
return false;
}
@ -923,6 +930,22 @@ export class InlineMenuFieldQualificationService
return !(this.passwordFieldExcludeListString.indexOf(cleanedValue) > -1);
}
/**
* Validates whether the provided field is a TOTP field.
*
* @param field - The field to validate
*/
private isTotpField = (field: AutofillField): boolean => {
if (this.fieldContainsAutocompleteValues(field, this.totpFieldAutocompleteValue)) {
return true;
}
return (
!this.isExcludedFieldType(field, this.excludedAutofillFieldTypesSet) &&
this.keywordsFoundInFieldData(field, AutoFillConstants.TotpFieldNames)
);
};
/**
* Validates the provided field to indicate if the field has a
* disqualifying attribute that would impede autofill entirely.