1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +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:
Ben Brooks
2025-08-04 11:02:15 -07:00
committed by GitHub
parent fd3c2b9515
commit bddd81ce79
2 changed files with 87 additions and 2 deletions

View File

@@ -50,6 +50,15 @@ export class AutoFillConstants {
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 PasswordFieldExcludeList: string[] = [

View File

@@ -58,6 +58,8 @@ export class InlineMenuFieldQualificationService
"neue e-mail",
"pwdcheck",
];
private newEmailFieldKeywords = new Set(AutoFillConstants.NewEmailFieldKeywords);
private newsletterFormKeywords = new Set(AutoFillConstants.NewsletterFormNames);
private updatePasswordFieldKeywords = [
"update password",
"change password",
@@ -152,6 +154,61 @@ export class InlineMenuFieldQualificationService
private totpFieldAutocompleteValue = "one-time-code";
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() {
void Promise.all([
sendExtensionMessage("getInlineMenuFieldQualificationFeatureFlag"),
@@ -300,7 +357,11 @@ export class InlineMenuFieldQualificationService
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 {
// 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.
// 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)) {
const newPasswordFieldsInPageDetails = pageDetails.fields.filter(
(field) => field.viewable && this.isNewPasswordField(field),
@@ -415,6 +482,10 @@ export class InlineMenuFieldQualificationService
const parentForm = pageDetails.forms[field.form];
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
// with a password field. If that's the case, then we should assume that it is a form field element.
if (!parentForm) {
@@ -822,9 +893,14 @@ export class InlineMenuFieldQualificationService
* @param field - The field to validate
*/
isFieldForIdentityEmail = (field: AutofillField): boolean => {
if (this.isExplicitIdentityEmailField(field)) {
return true;
}
if (
this.fieldContainsAutocompleteValues(field, this.emailAutocompleteValue) ||
field.type === "email"
field.type === "email" ||
field.htmlName === "email"
) {
return true;
}