1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 09:43:23 +00:00

PM-24657 - Cannot fill TOTP code into reddit.com during login (#16218)

* PM-24657 - exclude backup fields from totp qualifiers and autofill

* add tests for htmlID and htmlName for backup code exclusion

* add comments to justify tel addition

* Update apps/browser/src/autofill/services/autofill.service.ts

Co-authored-by: Jonathan Prusik <jprusik@users.noreply.github.com>

* update constant name to match recovery codes

---------

Co-authored-by: Jonathan Prusik <jprusik@users.noreply.github.com>
This commit is contained in:
Daniel Riera
2025-09-09 10:33:06 -04:00
committed by GitHub
parent 8e0bba689e
commit 57d6e3843f
4 changed files with 51 additions and 5 deletions

View File

@@ -46,6 +46,8 @@ export class AutoFillConstants {
"verification code", "verification code",
]; ];
static readonly RecoveryCodeFieldNames: string[] = ["backup", "recovery"];
static readonly AmbiguousTotpFieldNames: string[] = ["code", "pin", "otc", "otp", "2fa", "mfa"]; static readonly AmbiguousTotpFieldNames: string[] = ["code", "pin", "otc", "otp", "2fa", "mfa"];
static readonly SearchFieldNames: string[] = ["search", "query", "find", "go"]; static readonly SearchFieldNames: string[] = ["search", "query", "find", "go"];

View File

@@ -4487,6 +4487,34 @@ describe("AutofillService", () => {
expect(result).toBe(totpField); expect(result).toBe(totpField);
}); });
it("returns null if the totp field matches excluded TOTP field names via htmlID", () => {
totpField.htmlID = "recovery-code";
const result = autofillService["findTotpField"](
pageDetails,
passwordField,
false,
false,
false,
);
expect(result).toBeNull();
});
it("returns null if the totp field matches excluded TOTP field names via htmlName", () => {
totpField.htmlName = "backup";
const result = autofillService["findTotpField"](
pageDetails,
passwordField,
false,
false,
false,
);
expect(result).toBeNull();
});
}); });
describe("findMatchingFieldIndex", () => { describe("findMatchingFieldIndex", () => {

View File

@@ -948,7 +948,8 @@ export default class AutofillService implements AutofillServiceInterface {
...AutoFillConstants.TotpFieldNames, ...AutoFillConstants.TotpFieldNames,
...AutoFillConstants.AmbiguousTotpFieldNames, ...AutoFillConstants.AmbiguousTotpFieldNames,
]) || ]) ||
field.autoCompleteType === "one-time-code"); field.autoCompleteType === "one-time-code") &&
!AutofillService.fieldIsFuzzyMatch(field, [...AutoFillConstants.RecoveryCodeFieldNames]);
const isFillableUsernameField = const isFillableUsernameField =
!options.skipUsernameOnlyFill && !options.skipUsernameOnlyFill &&
@@ -2357,11 +2358,15 @@ export default class AutofillService implements AutofillServiceInterface {
(canBeReadOnly || !f.readonly) && (canBeReadOnly || !f.readonly) &&
(withoutForm || f.form === passwordField.form) && (withoutForm || f.form === passwordField.form) &&
(canBeHidden || f.viewable) && (canBeHidden || f.viewable) &&
(f.type === "text" || f.type === "number") && (f.type === "text" ||
f.type === "number" ||
// sites will commonly use tel in order to get the digit pad against semantic recommendations
f.type === "tel") &&
AutofillService.fieldIsFuzzyMatch(f, [ AutofillService.fieldIsFuzzyMatch(f, [
...AutoFillConstants.TotpFieldNames, ...AutoFillConstants.TotpFieldNames,
...AutoFillConstants.AmbiguousTotpFieldNames, ...AutoFillConstants.AmbiguousTotpFieldNames,
]) ]) &&
!AutofillService.fieldIsFuzzyMatch(f, [...AutoFillConstants.RecoveryCodeFieldNames])
) { ) {
totpField = f; totpField = f;
@@ -2551,7 +2556,7 @@ export default class AutofillService implements AutofillServiceInterface {
if (!AutofillService.hasValue(value)) { if (!AutofillService.hasValue(value)) {
continue; continue;
} }
if (this.fuzzyMatch(names, value)) { if (AutofillService.fuzzyMatch(names, value)) {
return showMatch ? [true, { attr, value }] : true; return showMatch ? [true, { attr, value }] : true;
} }
} }
@@ -2567,7 +2572,13 @@ export default class AutofillService implements AutofillServiceInterface {
* @private * @private
*/ */
private static fuzzyMatch(options: string[], value: string): boolean { private static fuzzyMatch(options: string[], value: string): boolean {
if (options == null || options.length === 0 || value == null || value === "") { if (
options == null ||
options.length === 0 ||
value == null ||
typeof value !== "string" ||
value.length < 1
) {
return false; return false;
} }

View File

@@ -16,6 +16,7 @@ import {
SubmitChangePasswordButtonNames, SubmitChangePasswordButtonNames,
SubmitLoginButtonNames, SubmitLoginButtonNames,
} from "./autofill-constants"; } from "./autofill-constants";
import AutofillService from "./autofill.service";
export class InlineMenuFieldQualificationService export class InlineMenuFieldQualificationService
implements InlineMenuFieldQualificationServiceInterface implements InlineMenuFieldQualificationServiceInterface
@@ -1071,6 +1072,10 @@ export class InlineMenuFieldQualificationService
* @param field - The field to validate * @param field - The field to validate
*/ */
isTotpField = (field: AutofillField): boolean => { isTotpField = (field: AutofillField): boolean => {
if (AutofillService.fieldIsFuzzyMatch(field, [...AutoFillConstants.RecoveryCodeFieldNames])) {
return false;
}
if (this.fieldContainsAutocompleteValues(field, this.totpFieldAutocompleteValue)) { if (this.fieldContainsAutocompleteValues(field, this.totpFieldAutocompleteValue)) {
return true; return true;
} }