diff --git a/apps/browser/src/autofill/services/abstractions/inline-menu-field-qualifications.service.ts b/apps/browser/src/autofill/services/abstractions/inline-menu-field-qualifications.service.ts index 58cfc25316..ecf8b924b1 100644 --- a/apps/browser/src/autofill/services/abstractions/inline-menu-field-qualifications.service.ts +++ b/apps/browser/src/autofill/services/abstractions/inline-menu-field-qualifications.service.ts @@ -13,6 +13,7 @@ export type SubmitButtonKeywordsMap = WeakMap; 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; diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index a78e9e0a6e..23a4fc2700 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -87,6 +87,11 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ destroyAutofillInlineMenuListeners: () => this.destroy(), getFormFieldDataForNotification: () => this.handleGetFormFieldDataForNotificationMessage(), }; + private readonly loginFieldQualifiers: Record = { + [AutofillFieldQualifier.username]: this.inlineMenuFieldQualificationService.isUsernameField, + [AutofillFieldQualifier.password]: + this.inlineMenuFieldQualificationService.isCurrentPasswordField, + }; private readonly cardFieldQualifiers: Record = { [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; - return; + for (const [fieldQualifier, fieldQualifierFunction] of Object.entries( + this.loginFieldQualifiers, + )) { + if (fieldQualifierFunction(autofillFieldData)) { + autofillFieldData.fieldQualifier = fieldQualifier as AutofillFieldQualifierType; + return; + } } - - autofillFieldData.fieldQualifier = AutofillFieldQualifier.username; } /** diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts index 21bdcba336..f38994e985 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts @@ -21,12 +21,29 @@ describe("InlineMenuFieldQualificationService", () => { }); describe("isFieldForLoginForm", () => { + it("disqualifies totp fields", () => { + const field = mock({ + 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({ 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( diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts index 67bf94e5cb..0b04b83ce4 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts @@ -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.