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:
parent
ebf51ebaaf
commit
10d503c15f
@ -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", () => {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user