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 { export interface InlineMenuFieldQualificationService {
isUsernameField(field: AutofillField): boolean; isUsernameField(field: AutofillField): boolean;
isCurrentPasswordField(field: AutofillField): boolean;
isNewPasswordField(field: AutofillField): boolean; isNewPasswordField(field: AutofillField): boolean;
isEmailField(field: AutofillField): boolean; isEmailField(field: AutofillField): boolean;
isFieldForLoginForm(field: AutofillField, pageDetails: AutofillPageDetails): boolean; isFieldForLoginForm(field: AutofillField, pageDetails: AutofillPageDetails): boolean;

View File

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

View File

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

View File

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