mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 00:03:56 +00:00
fix: [PM-23494] detect ID vs login inputs on booksamillion.com (#15548)
* fix: [PM-23494] add newsletter checks; cleanup Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * fix: [PM-23494] improve isExplicitIdentityEmailField, add NewEmailFieldKeywords Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> * fix: [PM-23494] improve isNewsletterForm, add NewsletterFormNames Signed-off-by: Ben Brooks <bbrooks@bitwarden.com> --------- Signed-off-by: Ben Brooks <bbrooks@bitwarden.com>
This commit is contained in:
@@ -50,6 +50,15 @@ export class AutoFillConstants {
|
|||||||
|
|
||||||
static readonly SearchFieldNames: string[] = ["search", "query", "find", "go"];
|
static readonly SearchFieldNames: string[] = ["search", "query", "find", "go"];
|
||||||
|
|
||||||
|
static readonly NewEmailFieldKeywords: string[] = [
|
||||||
|
"new-email",
|
||||||
|
"newemail",
|
||||||
|
"new email",
|
||||||
|
"neue e-mail",
|
||||||
|
];
|
||||||
|
|
||||||
|
static readonly NewsletterFormNames: string[] = ["newsletter"];
|
||||||
|
|
||||||
static readonly FieldIgnoreList: string[] = ["captcha", "findanything", "forgot"];
|
static readonly FieldIgnoreList: string[] = ["captcha", "findanything", "forgot"];
|
||||||
|
|
||||||
static readonly PasswordFieldExcludeList: string[] = [
|
static readonly PasswordFieldExcludeList: string[] = [
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ export class InlineMenuFieldQualificationService
|
|||||||
"neue e-mail",
|
"neue e-mail",
|
||||||
"pwdcheck",
|
"pwdcheck",
|
||||||
];
|
];
|
||||||
|
private newEmailFieldKeywords = new Set(AutoFillConstants.NewEmailFieldKeywords);
|
||||||
|
private newsletterFormKeywords = new Set(AutoFillConstants.NewsletterFormNames);
|
||||||
private updatePasswordFieldKeywords = [
|
private updatePasswordFieldKeywords = [
|
||||||
"update password",
|
"update password",
|
||||||
"change password",
|
"change password",
|
||||||
@@ -152,6 +154,61 @@ export class InlineMenuFieldQualificationService
|
|||||||
private totpFieldAutocompleteValue = "one-time-code";
|
private totpFieldAutocompleteValue = "one-time-code";
|
||||||
private premiumEnabled = false;
|
private premiumEnabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the provided field to indicate if the field is a new email field used for account creation/registration.
|
||||||
|
*
|
||||||
|
* @param field - The field to validate
|
||||||
|
*/
|
||||||
|
private isExplicitIdentityEmailField(field: AutofillField): boolean {
|
||||||
|
const matchFieldAttributeValues = [field.type, field.htmlName, field.htmlID, field.placeholder];
|
||||||
|
for (let attrIndex = 0; attrIndex < matchFieldAttributeValues.length; attrIndex++) {
|
||||||
|
if (!matchFieldAttributeValues[attrIndex]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let keywordIndex = 0; keywordIndex < matchFieldAttributeValues.length; keywordIndex++) {
|
||||||
|
if (this.newEmailFieldKeywords.has(matchFieldAttributeValues[attrIndex])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the provided form to indicate if the form is related to newsletter registration.
|
||||||
|
*
|
||||||
|
* @param parentForm - The form to validate
|
||||||
|
*/
|
||||||
|
private isNewsletterForm(parentForm: any): boolean {
|
||||||
|
if (!parentForm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchFieldAttributeValues = [
|
||||||
|
parentForm.type,
|
||||||
|
parentForm.htmlName,
|
||||||
|
parentForm.htmlID,
|
||||||
|
parentForm.placeholder,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let attrIndex = 0; attrIndex < matchFieldAttributeValues.length; attrIndex++) {
|
||||||
|
const attrValue = matchFieldAttributeValues[attrIndex];
|
||||||
|
if (!attrValue || typeof attrValue !== "string") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const attrValueLower = attrValue.toLowerCase();
|
||||||
|
for (const keyword of this.newsletterFormKeywords) {
|
||||||
|
if (attrValueLower.includes(keyword.toLowerCase())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
void Promise.all([
|
void Promise.all([
|
||||||
sendExtensionMessage("getInlineMenuFieldQualificationFeatureFlag"),
|
sendExtensionMessage("getInlineMenuFieldQualificationFeatureFlag"),
|
||||||
@@ -300,7 +357,11 @@ export class InlineMenuFieldQualificationService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.fieldContainsAutocompleteValues(field, this.identityAutocompleteValues);
|
return (
|
||||||
|
// Recognize explicit identity email fields (like id="new-email")
|
||||||
|
this.isFieldForIdentityEmail(field) ||
|
||||||
|
this.fieldContainsAutocompleteValues(field, this.identityAutocompleteValues)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -397,6 +458,12 @@ export class InlineMenuFieldQualificationService
|
|||||||
): boolean {
|
): boolean {
|
||||||
// If the provided field is set with an autocomplete of "username", we should assume that
|
// If the provided field is set with an autocomplete of "username", we should assume that
|
||||||
// the page developer intends for this field to be interpreted as a username field.
|
// the page developer intends for this field to be interpreted as a username field.
|
||||||
|
|
||||||
|
// Exclude non-login email field from being treated as a login username field
|
||||||
|
if (this.isExplicitIdentityEmailField(field)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.fieldContainsAutocompleteValues(field, this.loginUsernameAutocompleteValues)) {
|
if (this.fieldContainsAutocompleteValues(field, this.loginUsernameAutocompleteValues)) {
|
||||||
const newPasswordFieldsInPageDetails = pageDetails.fields.filter(
|
const newPasswordFieldsInPageDetails = pageDetails.fields.filter(
|
||||||
(field) => field.viewable && this.isNewPasswordField(field),
|
(field) => field.viewable && this.isNewPasswordField(field),
|
||||||
@@ -415,6 +482,10 @@ export class InlineMenuFieldQualificationService
|
|||||||
const parentForm = pageDetails.forms[field.form];
|
const parentForm = pageDetails.forms[field.form];
|
||||||
const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isCurrentPasswordField);
|
const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isCurrentPasswordField);
|
||||||
|
|
||||||
|
if (this.isNewsletterForm(parentForm)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If the field is not structured within a form, we need to identify if the field is used in conjunction
|
// If the field is not structured within a form, we need to identify if the field is used in conjunction
|
||||||
// with a password field. If that's the case, then we should assume that it is a form field element.
|
// with a password field. If that's the case, then we should assume that it is a form field element.
|
||||||
if (!parentForm) {
|
if (!parentForm) {
|
||||||
@@ -822,9 +893,14 @@ export class InlineMenuFieldQualificationService
|
|||||||
* @param field - The field to validate
|
* @param field - The field to validate
|
||||||
*/
|
*/
|
||||||
isFieldForIdentityEmail = (field: AutofillField): boolean => {
|
isFieldForIdentityEmail = (field: AutofillField): boolean => {
|
||||||
|
if (this.isExplicitIdentityEmailField(field)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.fieldContainsAutocompleteValues(field, this.emailAutocompleteValue) ||
|
this.fieldContainsAutocompleteValues(field, this.emailAutocompleteValue) ||
|
||||||
field.type === "email"
|
field.type === "email" ||
|
||||||
|
field.htmlName === "email"
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user