1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-21 16:18:28 +01:00

[PM-6501] Search field disqualifications preventing filling password input fields (#8117)

* [PM-6501] Search field disqualifications preventing filling password input fields

* [PM-6501] Reworking implementation of AutofillService.isSearchField to more carefully test search field attribute keywords

* [PM-6501] Reworking implementation of AutofillService.isSearchField to more carefully test search field attribute keywords

* [PM-6501] Reworking implementation of AutofillService.isSearchField to more carefully test search field attribute keywords
This commit is contained in:
Cesar Gonzalez 2024-03-14 11:06:07 -05:00 committed by GitHub
parent ebf51ebaaf
commit 10d503c15f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 25 deletions

View File

@ -3380,6 +3380,34 @@ describe("AutofillService", () => {
expect(value).toBe(false);
});
it("validates attribute identifiers with mixed camel case and non-alpha characters", () => {
const attributes: Record<string, boolean> = {
_$1_go_look: true,
go_look: true,
goLook: true,
go1look: true,
"go look": true,
look_go: true,
findPerson: true,
query$1: true,
look_goo: false,
golook: false,
lookgo: false,
logonField: false,
ego_input: false,
"Gold Password": false,
searching_for: false,
person_finder: false,
};
const autofillFieldMocks = Object.keys(attributes).map((key) =>
createAutofillFieldMock({ htmlID: key }),
);
autofillFieldMocks.forEach((field) => {
const value = AutofillService["isSearchField"](field);
expect(value).toBe(attributes[field.htmlID]);
});
});
});
describe("isFieldMatch", () => {

View File

@ -44,6 +44,7 @@ export default class AutofillService implements AutofillServiceInterface {
private openPasswordRepromptPopoutDebounce: NodeJS.Timeout;
private currentlyOpeningPasswordRepromptPopout = false;
private autofillScriptPortsSet = new Set<chrome.runtime.Port>();
static searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames);
constructor(
private cipherService: CipherService,
@ -1380,11 +1381,33 @@ export default class AutofillService implements AutofillServiceInterface {
return excludedTypes.indexOf(type) > -1;
}
/**
* Identifies if a passed field contains text artifacts that identify it as a search field.
*
* @param field - The autofill field that we are validating as a search field
*/
private static isSearchField(field: AutofillField) {
const matchFieldAttributeValues = [field.type, field.htmlName, field.htmlID, field.placeholder];
const matchPattern = new RegExp(AutoFillConstants.SearchFieldNames.join("|"), "gi");
for (let attrIndex = 0; attrIndex < matchFieldAttributeValues.length; attrIndex++) {
if (!matchFieldAttributeValues[attrIndex]) {
continue;
}
return Boolean(matchFieldAttributeValues.join(" ").match(matchPattern));
// Separate camel case words and case them to lower case values
const camelCaseSeparatedFieldAttribute = matchFieldAttributeValues[attrIndex]
.replace(/([a-z])([A-Z])/g, "$1 $2")
.toLowerCase();
// Split the attribute by non-alphabetical characters to get the keywords
const attributeKeywords = camelCaseSeparatedFieldAttribute.split(/[^a-z]/gi);
for (let keywordIndex = 0; keywordIndex < attributeKeywords.length; keywordIndex++) {
if (AutofillService.searchFieldNamesSet.has(attributeKeywords[keywordIndex])) {
return true;
}
}
}
return false;
}
static isExcludedFieldType(field: AutofillField, excludedTypes: string[]) {
@ -1397,11 +1420,7 @@ export default class AutofillService implements AutofillServiceInterface {
}
// Check if the input is an untyped/mistyped search input
if (this.isSearchField(field)) {
return true;
}
return false;
return this.isSearchField(field);
}
/**
@ -1525,11 +1544,7 @@ export default class AutofillService implements AutofillServiceInterface {
return false;
}
if (AutoFillConstants.PasswordFieldExcludeList.some((i) => cleanedValue.indexOf(i) > -1)) {
return false;
}
return true;
return !AutoFillConstants.PasswordFieldExcludeList.some((i) => cleanedValue.indexOf(i) > -1);
}
static fieldHasDisqualifyingAttributeValue(field: AutofillField) {
@ -1572,7 +1587,11 @@ export default class AutofillService implements AutofillServiceInterface {
const arr: AutofillField[] = [];
pageDetails.fields.forEach((f) => {
if (AutofillService.isExcludedFieldType(f, AutoFillConstants.ExcludedAutofillLoginTypes)) {
const isPassword = f.type === "password";
if (
!isPassword &&
AutofillService.isExcludedFieldType(f, AutoFillConstants.ExcludedAutofillLoginTypes)
) {
return;
}
@ -1581,23 +1600,16 @@ export default class AutofillService implements AutofillServiceInterface {
return;
}
const isPassword = f.type === "password";
const isLikePassword = () => {
if (f.type !== "text") {
return false;
}
if (AutofillService.valueIsLikePassword(f.htmlID)) {
return true;
}
if (AutofillService.valueIsLikePassword(f.htmlName)) {
return true;
}
if (AutofillService.valueIsLikePassword(f.placeholder)) {
return true;
const testedValues = [f.htmlID, f.htmlName, f.placeholder];
for (let i = 0; i < testedValues.length; i++) {
if (AutofillService.valueIsLikePassword(testedValues[i])) {
return true;
}
}
return false;