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:
parent
12b5ce548d
commit
4e1912e24e
@ -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;
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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(
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user