tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 86244193a4e232ea93e00f481db063ac6b37f732
parent 73f94bcfa38256c4d52abb8b1dde2619a0d8ce9d
Author: Dimi <dlee@mozilla.com>
Date:   Tue, 30 Sep 2025 12:22:16 +0000

Bug 1991068 - Infer username-only sign-in forms based on a form button r=joschmidt,credential-management-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D266336

Diffstat:
Mtoolkit/components/passwordmgr/LoginHelper.sys.mjs | 24++++++++++++++++++++----
Mtoolkit/components/passwordmgr/LoginManager.shared.sys.mjs | 4++++
Mtoolkit/components/passwordmgr/shared/NewPasswordModel.sys.mjs | 3+++
Mtoolkit/components/passwordmgr/test/unit/test_getUsernameFieldFromUsernameOnlyForm.js | 8++++++++
4 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/toolkit/components/passwordmgr/LoginHelper.sys.mjs b/toolkit/components/passwordmgr/LoginHelper.sys.mjs @@ -17,6 +17,7 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs", BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", + NewPasswordModel: "resource://gre/modules/shared/NewPasswordModel.sys.mjs", }); export class ParentAutocompleteOption { @@ -1298,11 +1299,26 @@ export const LoginHelper = { * @returns {boolean} True if any of the rules matches */ isInferredLoginForm(formElement) { - // This is copied from 'loginFormAttrRegex' in NewPasswordModel.sys.mjs - const loginExpr = - /login|log in|log on|log-on|sign in|sigin|sign\/in|sign-in|sign on|sign-on/i; + if ( + Logic.elementAttrsMatchRegex( + formElement, + lazy.NewPasswordModel.LoginRegex + ) + ) { + return true; + } - if (Logic.elementAttrsMatchRegex(formElement, loginExpr)) { + const buttons = Array.from( + formElement.querySelectorAll("button[type=submit]") + ); + // Limit to form with only one submit button to avoid false positives. + if ( + buttons.length == 1 && + Logic.hasTextContentMatchingRegex( + buttons[0], + lazy.NewPasswordModel.LoginFormAttrRegex + ) + ) { return true; } diff --git a/toolkit/components/passwordmgr/LoginManager.shared.sys.mjs b/toolkit/components/passwordmgr/LoginManager.shared.sys.mjs @@ -43,6 +43,10 @@ class Logic { return regex.test(element.labels?.[0]?.textContent); } + static hasTextContentMatchingRegex(element, regex) { + return regex.test(element.textContent); + } + /** * Get the parts of the URL we want for identification. * Strip out things like the userPass portion and handle javascript:. diff --git a/toolkit/components/passwordmgr/shared/NewPasswordModel.sys.mjs b/toolkit/components/passwordmgr/shared/NewPasswordModel.sys.mjs @@ -678,4 +678,7 @@ function makeRuleset(coeffs, biases) { export const NewPasswordModel = { type: "new", rules: makeRuleset([...coefficients.new], biases), + + LoginRegex: loginRegex, + LoginFormAttrRegex: loginFormAttrRegex, }; diff --git a/toolkit/components/passwordmgr/test/unit/test_getUsernameFieldFromUsernameOnlyForm.js b/toolkit/components/passwordmgr/test/unit/test_getUsernameFieldFromUsernameOnlyForm.js @@ -132,6 +132,14 @@ const TESTCASES = [ }, expectations: [false, false], }, + { + description: "Form with a sign-in button", + document: `<form> + <input id="un1" type="text"> + <button type="submit">Sign In</button> + </form>`, + expectations: [true, true], + }, ]; function _setPrefs() {