Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FormAnalyzer] scoring password hints #720

Merged
merged 21 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions dist/autofill-debug.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 29 additions & 7 deletions dist/autofill.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 22 additions & 4 deletions src/Form/FormAnalyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,18 @@ class FormAnalyzer {
return this;
}

areLoginOrSignupSignalsWeak() {
return Math.abs(this.autofillSignal) < 10;
}

/**
* Hybrid forms can be used for both login and signup
* @returns {boolean}
*/
get isHybrid() {
// When marking for hybrid we also want to ensure other signals are weak
const areOtherSignalsWeak = Math.abs(this.autofillSignal) < 10;

return this.hybridSignal > 0 && areOtherSignalsWeak;
return this.hybridSignal > 0 && this.areLoginOrSignupSignalsWeak();
}

get isLogin() {
Expand Down Expand Up @@ -146,7 +149,7 @@ class FormAnalyzer {
}

const signupRegexToUse = this.matching.getDDGMatcherRegex(shouldBeConservative ? 'conservativeSignupRegex' : 'signupRegex');
const matchesSignup = safeRegexTest(/new.?password/i, string) || safeRegexTest(signupRegexToUse, string);
const matchesSignup = safeRegexTest(/new.?(password|username)/i, string) || safeRegexTest(signupRegexToUse, string);

// In some cases a login match means the login is somewhere else, i.e. when a link points outside
if (shouldFlip) {
Expand Down Expand Up @@ -230,6 +233,16 @@ class FormAnalyzer {
});
}

evaluatePasswordHints() {
const textContent = removeExcessWhitespace(this.form.textContent, 200);
if (textContent) {
const hasPasswordHints = safeRegexTest(this.matching.getDDGMatcherRegex('passwordHintsRegex'), textContent, 500);
if (hasPasswordHints) {
this.increaseSignalBy(5, 'Password hints');
}
}
}

/**
* Function that checks if the element is link like and navigating away from the current page
* @param {any} el
Expand Down Expand Up @@ -345,11 +358,16 @@ class FormAnalyzer {
}

// A form with many fields is unlikely to be a login form
const relevantFields = this.form.querySelectorAll(this.matching.cssSelector('genericTextField'));
const relevantFields = this.form.querySelectorAll(this.matching.cssSelector('genericTextInputField'));
if (relevantFields.length >= 4) {
this.increaseSignalBy(relevantFields.length * 1.5, 'many fields: it is probably not a login');
}

// If we can't decide at this point, try reading password hints
if (this.areLoginOrSignupSignalsWeak()) {
this.evaluatePasswordHints();
}

// If we can't decide at this point, try reading page headings
if (this.autofillSignal === 0) {
this.evaluatePageHeadings();
Expand Down
13 changes: 12 additions & 1 deletion src/Form/input-classifiers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,17 @@ const getMismatchedValue = (score) => {
return '';
};

/**
* Cleans HTML content by removing excess whitespace and newlines between tags while preserving
* whitespace within text content
* @param {string} html The HTML content to clean
* @returns {string} The cleaned HTML
*/
const cleanFormHTML = (html) => {
// Collapse both whitespace and newlines between tags, preserving content within tags
return html.replace(/>\s*[\r\n]+\s*</g, '><');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d be worried about removing all whitespace between tags which might miss some new lines but also collapse words together we don’t expect.

Using the Apple password work as an example — document.querySelector('form.modal-form').textContent

https://account.apple.com/account/manage (copy is slightly different from our stored form)

PasswordLast updated 12 November 2020Change your password current passwordStrength:0%Password Requirements:at least 8 charactersat least 1 numberat least 1 uppercase letterat least 1 lowercase letterAt least one special characterAvoid using a password that you use with other websites or which might be easy for someone else to guess. new password confirm new passwordSign out of Apple devices and websites associated with your Apple Account. Learn more

Using the above

PasswordLast updated 11 March 2023Change your passwordcurrent
                    passwordStrength:0%.Password Requirements:at least 8 charactersat least 1 numberat least 1 uppercase letterat least 1 lowercase letterAvoid using a password that you use with other websites or which might be easy for someone else to guess.new
                            passwordconfirm
                    new passwordSign out of Apple devices and websites associated with your Apple ID. Learn more

From a quick stab at this, I think we can get pretty good results — remove the leading whitespace, remove new lines between elements, and then replace all remaining new lines with a space. It’s not 100%, any other enhancements we could add?

PasswordLast updated 11 March 2023Change your passwordcurrent passwordStrength:0%.Password Requirements:at least 8 charactersat least 1 numberat least 1 uppercase letterat least 1 lowercase letterAvoid using a password that you use with other websites or which might be easy for someone else to guess.new passwordconfirm new passwordSign out of Apple devices and websites associated with your Apple ID. Learn more
Suggested change
return html.replace(/>\s*[\r\n]+\s*</g, '><');
return html.
.replace(/^\s*/mg, "") // Remove leading whitespace on each line
.replace(/>[\r\n]+</mg, "><") // Remove new lines which exist between elements
.replace(/[\r\n]+/mg, " "); // Replace any existing new lines with a space

};

const isThereAMismatch = (score) => {
return Boolean(getMismatchedValue(score));
};
Expand Down Expand Up @@ -178,7 +189,7 @@ describe.each(testCases)('Test $html fields', (testCase) => {
document.body.appendChild(baseWrapper);
}

baseWrapper.innerHTML = testContent;
baseWrapper.innerHTML = cleanFormHTML(testContent);
document.title = title;

const matching = createMatching();
Expand Down
Loading
Loading