diff --git a/dist/autofill-debug.js b/dist/autofill-debug.js
index 3472e1d2a..22590f9a5 100644
--- a/dist/autofill-debug.js
+++ b/dist/autofill-debug.js
@@ -9262,7 +9262,7 @@ function initFormSubmissionsApi(forms, matching) {
const button = /** @type HTMLElement */event.target?.closest(selector);
if (!button) return;
const text = (0, _autofillUtils.getTextShallow)(button) || (0, _labelUtil.extractElementStrings)(button).join(' ');
- const hasRelevantText = matching.getDDGMatcherRegex('submitButtonRegex')?.test(text);
+ const hasRelevantText = (0, _autofillUtils.safeRegexTest)(matching.getDDGMatcherRegex('submitButtonRegex'), text);
if (hasRelevantText && text.length < 25) {
// check if there's a form with values
const filledForm = [...forms.values()].find(form => form.hasValues());
@@ -9287,7 +9287,7 @@ function initFormSubmissionsApi(forms, matching) {
const observer = new PerformanceObserver(list => {
const entries = list.getEntries().filter(entry =>
// @ts-ignore why does TS not know about `entry.initiatorType`?
- ['fetch', 'xmlhttprequest'].includes(entry.initiatorType) && /login|sign-in|signin/.test(entry.name));
+ ['fetch', 'xmlhttprequest'].includes(entry.initiatorType) && (0, _autofillUtils.safeRegexTest)(/login|sign-in|signin/, entry.name));
if (!entries.length) return;
const filledForm = [...forms.values()].find(form => form.hasValues());
const focusedForm = [...forms.values()].find(form => form.hasFocus());
@@ -9577,7 +9577,7 @@ class Form {
const probableField = hiddenFields.find(field => {
const regex = new RegExp('email|' + this.matching.getDDGMatcherRegex('username')?.source);
const attributeText = field.id + ' ' + field.name;
- return regex?.test(attributeText);
+ return (0, _autofillUtils.safeRegexTest)(regex, attributeText);
});
if (probableField?.value) {
formValues.credentials.username = probableField.value;
@@ -10317,15 +10317,15 @@ class FormAnalyzer {
} = _ref;
// If the string is empty or too long (noisy) do nothing
if (!string || string.length > _constants.constants.TEXT_LENGTH_CUTOFF) return this;
- const matchesLogin = /current.?password/i.test(string) || this.matching.getDDGMatcherRegex('loginRegex')?.test(string) || this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string);
+ const matchesLogin = (0, _autofillUtils.safeRegexTest)(/current.?password/i, string) || (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginRegex'), string) || (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('resetPasswordLink'), string);
// Check explicitly for unified login/signup forms
- if (shouldCheckUnifiedForm && matchesLogin && this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(string)) {
+ if (shouldCheckUnifiedForm && matchesLogin && (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), string)) {
this.increaseHybridSignal(strength, signalType);
return this;
}
const signupRegexToUse = this.matching.getDDGMatcherRegex(shouldBeConservative ? 'conservativeSignupRegex' : 'signupRegex');
- const matchesSignup = /new.?password/i.test(string) || signupRegexToUse?.test(string);
+ const matchesSignup = (0, _autofillUtils.safeRegexTest)(/new.?password/i, string) || (0, _autofillUtils.safeRegexTest)(signupRegexToUse, string);
// In some cases a login match means the login is somewhere else, i.e. when a link points outside
if (shouldFlip) {
@@ -10353,8 +10353,8 @@ class FormAnalyzer {
}
evaluateUrl() {
const path = window.location.pathname;
- const matchesLogin = this.matching.getDDGMatcherRegex('loginRegex')?.test(path);
- const matchesSignup = this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(path);
+ const matchesLogin = (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginRegex'), path);
+ const matchesSignup = (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), path);
// If the url matches both, do nothing: the signal is probably confounding
if (matchesLogin && matchesSignup) return;
@@ -10439,10 +10439,10 @@ class FormAnalyzer {
let shouldFlip = true;
let strength = 1;
// Don't flip forgotten password links
- if (this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string)) {
+ if ((0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('resetPasswordLink'), string)) {
shouldFlip = false;
strength = 3;
- } else if (this.matching.getDDGMatcherRegex('loginProvidersRegex')?.test(string)) {
+ } else if ((0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginProvidersRegex'), string)) {
// Don't flip login providers links
shouldFlip = false;
}
@@ -10521,7 +10521,7 @@ class FormAnalyzer {
name,
value
} = _ref2;
- return /(credit|payment).?card/i.test(`${name}=${value}`);
+ return (0, _autofillUtils.safeRegexTest)(/(credit|payment).?card/i, `${name}=${value}`);
});
if (hasCCAttribute) {
this._isCCForm = true;
@@ -12811,7 +12811,7 @@ class Matching {
matched: false
};
}
- if (notRegex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(notRegex, elementString)) {
return {
...result,
matched: false,
@@ -12830,7 +12830,7 @@ class Matching {
matched: false
};
}
- if (skipRegex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(skipRegex, elementString)) {
return {
...result,
matched: false,
@@ -12840,7 +12840,7 @@ class Matching {
}
// if the `match` regex fails, moves onto the next string
- if (!matchRexExp.test(elementString)) {
+ if (!(0, _autofillUtils.safeRegexTest)(matchRexExp, elementString)) {
continue;
}
@@ -12891,7 +12891,7 @@ class Matching {
for (let stringName of stringsToMatch) {
let elementString = this.activeElementStrings[stringName];
if (!elementString) continue;
- if (regex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(regex, elementString)) {
return {
...defaultResult,
matched: true,
@@ -15867,13 +15867,16 @@ exports.isLocalNetwork = isLocalNetwork;
exports.isPotentiallyViewable = void 0;
exports.isValidTLD = isValidTLD;
exports.logPerformance = logPerformance;
-exports.setValue = exports.sendAndWaitForAnswer = exports.safeExecute = exports.removeInlineStyles = exports.notifyWebApp = void 0;
+exports.safeExecute = exports.removeInlineStyles = exports.notifyWebApp = void 0;
+exports.safeRegexTest = safeRegexTest;
+exports.setValue = exports.sendAndWaitForAnswer = void 0;
exports.shouldLog = shouldLog;
exports.shouldLogPerformance = shouldLogPerformance;
exports.truncateFromMiddle = truncateFromMiddle;
exports.wasAutofilledByChrome = void 0;
exports.whenIdle = whenIdle;
var _matching = require("./Form/matching.js");
+var _constants = require("./constants.js");
const SIGN_IN_MSG = exports.SIGN_IN_MSG = {
signMeIn: true
};
@@ -16226,15 +16229,15 @@ const isLikelyASubmitButton = (el, matching) => {
// is explicitly set as "submit"
el.getAttribute('name') === 'submit') &&
// is called "submit"
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel)) return true;
- return (/primary|submit/i.test(el.className) ||
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel)) return true;
+ return (safeRegexTest(/primary|submit/i, el.className) ||
// has high-signal submit classes
- /submit/i.test(dataTestId) || matching.getDDGMatcherRegex('submitButtonRegex')?.test(text) ||
+ safeRegexTest(/submit/i, dataTestId) || safeRegexTest(matching.getDDGMatcherRegex('submitButtonRegex'), text) ||
// has high-signal text
- el.offsetHeight * el.offsetWidth >= 10000 && !/secondary/i.test(el.className) // it's a large element 250x40px
+ el.offsetHeight * el.offsetWidth >= 10000 && !safeRegexTest(/secondary/i, el.className) // it's a large element 250x40px
) && el.offsetHeight * el.offsetWidth >= 2000 &&
// it's not a very small button like inline links and such
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel);
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel);
};
/**
@@ -16245,9 +16248,9 @@ const isLikelyASubmitButton = (el, matching) => {
exports.isLikelyASubmitButton = isLikelyASubmitButton;
const buttonMatchesFormType = (el, formObj) => {
if (formObj.isLogin) {
- return !/sign.?up|register|join/i.test(el.textContent || '');
+ return !safeRegexTest(/sign.?up|register|join/i, el.textContent || '');
} else if (formObj.isSignup) {
- return !/(log|sign).?([io])n/i.test(el.textContent || '');
+ return !safeRegexTest(/(log|sign).?([io])n/i, el.textContent || '');
} else {
return true;
}
@@ -16408,7 +16411,18 @@ function isFormLikelyToBeUsedAsPageWrapper(form) {
return formChildrenPercentage > 50;
}
-},{"./Form/matching.js":43}],62:[function(require,module,exports){
+/**
+ * Wrapper around RegExp.test that safeguard against checking huge strings
+ * @param {RegExp | undefined} regex
+ * @param {String} string
+ * @returns {boolean}
+ */
+function safeRegexTest(regex, string) {
+ if (!string || !regex || string.length > _constants.constants.TEXT_LENGTH_CUTOFF) return false;
+ return regex.test(string);
+}
+
+},{"./Form/matching.js":43,"./constants.js":64}],62:[function(require,module,exports){
"use strict";
require("./requestIdleCallback.js");
diff --git a/dist/autofill.js b/dist/autofill.js
index 8fa1a238c..bd228ac95 100644
--- a/dist/autofill.js
+++ b/dist/autofill.js
@@ -5317,7 +5317,7 @@ function initFormSubmissionsApi(forms, matching) {
const button = /** @type HTMLElement */event.target?.closest(selector);
if (!button) return;
const text = (0, _autofillUtils.getTextShallow)(button) || (0, _labelUtil.extractElementStrings)(button).join(' ');
- const hasRelevantText = matching.getDDGMatcherRegex('submitButtonRegex')?.test(text);
+ const hasRelevantText = (0, _autofillUtils.safeRegexTest)(matching.getDDGMatcherRegex('submitButtonRegex'), text);
if (hasRelevantText && text.length < 25) {
// check if there's a form with values
const filledForm = [...forms.values()].find(form => form.hasValues());
@@ -5342,7 +5342,7 @@ function initFormSubmissionsApi(forms, matching) {
const observer = new PerformanceObserver(list => {
const entries = list.getEntries().filter(entry =>
// @ts-ignore why does TS not know about `entry.initiatorType`?
- ['fetch', 'xmlhttprequest'].includes(entry.initiatorType) && /login|sign-in|signin/.test(entry.name));
+ ['fetch', 'xmlhttprequest'].includes(entry.initiatorType) && (0, _autofillUtils.safeRegexTest)(/login|sign-in|signin/, entry.name));
if (!entries.length) return;
const filledForm = [...forms.values()].find(form => form.hasValues());
const focusedForm = [...forms.values()].find(form => form.hasFocus());
@@ -5632,7 +5632,7 @@ class Form {
const probableField = hiddenFields.find(field => {
const regex = new RegExp('email|' + this.matching.getDDGMatcherRegex('username')?.source);
const attributeText = field.id + ' ' + field.name;
- return regex?.test(attributeText);
+ return (0, _autofillUtils.safeRegexTest)(regex, attributeText);
});
if (probableField?.value) {
formValues.credentials.username = probableField.value;
@@ -6372,15 +6372,15 @@ class FormAnalyzer {
} = _ref;
// If the string is empty or too long (noisy) do nothing
if (!string || string.length > _constants.constants.TEXT_LENGTH_CUTOFF) return this;
- const matchesLogin = /current.?password/i.test(string) || this.matching.getDDGMatcherRegex('loginRegex')?.test(string) || this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string);
+ const matchesLogin = (0, _autofillUtils.safeRegexTest)(/current.?password/i, string) || (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginRegex'), string) || (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('resetPasswordLink'), string);
// Check explicitly for unified login/signup forms
- if (shouldCheckUnifiedForm && matchesLogin && this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(string)) {
+ if (shouldCheckUnifiedForm && matchesLogin && (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), string)) {
this.increaseHybridSignal(strength, signalType);
return this;
}
const signupRegexToUse = this.matching.getDDGMatcherRegex(shouldBeConservative ? 'conservativeSignupRegex' : 'signupRegex');
- const matchesSignup = /new.?password/i.test(string) || signupRegexToUse?.test(string);
+ const matchesSignup = (0, _autofillUtils.safeRegexTest)(/new.?password/i, string) || (0, _autofillUtils.safeRegexTest)(signupRegexToUse, string);
// In some cases a login match means the login is somewhere else, i.e. when a link points outside
if (shouldFlip) {
@@ -6408,8 +6408,8 @@ class FormAnalyzer {
}
evaluateUrl() {
const path = window.location.pathname;
- const matchesLogin = this.matching.getDDGMatcherRegex('loginRegex')?.test(path);
- const matchesSignup = this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(path);
+ const matchesLogin = (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginRegex'), path);
+ const matchesSignup = (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), path);
// If the url matches both, do nothing: the signal is probably confounding
if (matchesLogin && matchesSignup) return;
@@ -6494,10 +6494,10 @@ class FormAnalyzer {
let shouldFlip = true;
let strength = 1;
// Don't flip forgotten password links
- if (this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string)) {
+ if ((0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('resetPasswordLink'), string)) {
shouldFlip = false;
strength = 3;
- } else if (this.matching.getDDGMatcherRegex('loginProvidersRegex')?.test(string)) {
+ } else if ((0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginProvidersRegex'), string)) {
// Don't flip login providers links
shouldFlip = false;
}
@@ -6576,7 +6576,7 @@ class FormAnalyzer {
name,
value
} = _ref2;
- return /(credit|payment).?card/i.test(`${name}=${value}`);
+ return (0, _autofillUtils.safeRegexTest)(/(credit|payment).?card/i, `${name}=${value}`);
});
if (hasCCAttribute) {
this._isCCForm = true;
@@ -8866,7 +8866,7 @@ class Matching {
matched: false
};
}
- if (notRegex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(notRegex, elementString)) {
return {
...result,
matched: false,
@@ -8885,7 +8885,7 @@ class Matching {
matched: false
};
}
- if (skipRegex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(skipRegex, elementString)) {
return {
...result,
matched: false,
@@ -8895,7 +8895,7 @@ class Matching {
}
// if the `match` regex fails, moves onto the next string
- if (!matchRexExp.test(elementString)) {
+ if (!(0, _autofillUtils.safeRegexTest)(matchRexExp, elementString)) {
continue;
}
@@ -8946,7 +8946,7 @@ class Matching {
for (let stringName of stringsToMatch) {
let elementString = this.activeElementStrings[stringName];
if (!elementString) continue;
- if (regex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(regex, elementString)) {
return {
...defaultResult,
matched: true,
@@ -11922,13 +11922,16 @@ exports.isLocalNetwork = isLocalNetwork;
exports.isPotentiallyViewable = void 0;
exports.isValidTLD = isValidTLD;
exports.logPerformance = logPerformance;
-exports.setValue = exports.sendAndWaitForAnswer = exports.safeExecute = exports.removeInlineStyles = exports.notifyWebApp = void 0;
+exports.safeExecute = exports.removeInlineStyles = exports.notifyWebApp = void 0;
+exports.safeRegexTest = safeRegexTest;
+exports.setValue = exports.sendAndWaitForAnswer = void 0;
exports.shouldLog = shouldLog;
exports.shouldLogPerformance = shouldLogPerformance;
exports.truncateFromMiddle = truncateFromMiddle;
exports.wasAutofilledByChrome = void 0;
exports.whenIdle = whenIdle;
var _matching = require("./Form/matching.js");
+var _constants = require("./constants.js");
const SIGN_IN_MSG = exports.SIGN_IN_MSG = {
signMeIn: true
};
@@ -12281,15 +12284,15 @@ const isLikelyASubmitButton = (el, matching) => {
// is explicitly set as "submit"
el.getAttribute('name') === 'submit') &&
// is called "submit"
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel)) return true;
- return (/primary|submit/i.test(el.className) ||
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel)) return true;
+ return (safeRegexTest(/primary|submit/i, el.className) ||
// has high-signal submit classes
- /submit/i.test(dataTestId) || matching.getDDGMatcherRegex('submitButtonRegex')?.test(text) ||
+ safeRegexTest(/submit/i, dataTestId) || safeRegexTest(matching.getDDGMatcherRegex('submitButtonRegex'), text) ||
// has high-signal text
- el.offsetHeight * el.offsetWidth >= 10000 && !/secondary/i.test(el.className) // it's a large element 250x40px
+ el.offsetHeight * el.offsetWidth >= 10000 && !safeRegexTest(/secondary/i, el.className) // it's a large element 250x40px
) && el.offsetHeight * el.offsetWidth >= 2000 &&
// it's not a very small button like inline links and such
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel);
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel);
};
/**
@@ -12300,9 +12303,9 @@ const isLikelyASubmitButton = (el, matching) => {
exports.isLikelyASubmitButton = isLikelyASubmitButton;
const buttonMatchesFormType = (el, formObj) => {
if (formObj.isLogin) {
- return !/sign.?up|register|join/i.test(el.textContent || '');
+ return !safeRegexTest(/sign.?up|register|join/i, el.textContent || '');
} else if (formObj.isSignup) {
- return !/(log|sign).?([io])n/i.test(el.textContent || '');
+ return !safeRegexTest(/(log|sign).?([io])n/i, el.textContent || '');
} else {
return true;
}
@@ -12463,7 +12466,18 @@ function isFormLikelyToBeUsedAsPageWrapper(form) {
return formChildrenPercentage > 50;
}
-},{"./Form/matching.js":33}],52:[function(require,module,exports){
+/**
+ * Wrapper around RegExp.test that safeguard against checking huge strings
+ * @param {RegExp | undefined} regex
+ * @param {String} string
+ * @returns {boolean}
+ */
+function safeRegexTest(regex, string) {
+ if (!string || !regex || string.length > _constants.constants.TEXT_LENGTH_CUTOFF) return false;
+ return regex.test(string);
+}
+
+},{"./Form/matching.js":33,"./constants.js":54}],52:[function(require,module,exports){
"use strict";
require("./requestIdleCallback.js");
diff --git a/integration-test/helpers/harness.js b/integration-test/helpers/harness.js
index 230b26cfc..d19401866 100644
--- a/integration-test/helpers/harness.js
+++ b/integration-test/helpers/harness.js
@@ -215,7 +215,7 @@ export function forwardConsoleMessages (page, _opts = {}) {
*/
export async function performanceEntries (page, measureName) {
// don't measure until the entries exist
- await page.waitForFunction((measureName) => window.performance.getEntriesByName(measureName).length > 0, `${measureName}:end`)
+ await page.waitForFunction((measureName) => window.performance.getEntriesByName(measureName).length > 0, `${measureName}:end`, { timeout: 5000 })
const result = await page.evaluate((measureName) => {
window.performance?.measure?.(measureName, `${measureName}:start`, `${measureName}:end`)
const entries = window.performance?.getEntriesByName(measureName)
diff --git a/integration-test/helpers/mocks.js b/integration-test/helpers/mocks.js
index 6e62c6109..c8b035c26 100644
--- a/integration-test/helpers/mocks.js
+++ b/integration-test/helpers/mocks.js
@@ -8,6 +8,7 @@ export const constants = {
'emailAtBottom': 'pages/email-at-bottom.html',
'emailAtTopLeft': 'pages/email-at-top-left.html',
'scanner-perf': 'pages/scanner-perf.html',
+ 'perf-huge-regex': 'pages/perf-huge-regex.html',
'iframeContainer': 'pages/iframe-container.html',
'signup': 'pages/signup.html',
'mutatingForm': 'pages/mutating-form.html',
diff --git a/integration-test/pages/perf-huge-regex.html b/integration-test/pages/perf-huge-regex.html
new file mode 100644
index 000000000..ef9b2c6e9
--- /dev/null
+++ b/integration-test/pages/perf-huge-regex.html
@@ -0,0 +1,4315 @@
+
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Služby SME:
+
+
+
Nákupy SME:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration-test/tests/email-autofill.macos.spec.js b/integration-test/tests/email-autofill.macos.spec.js
index db382843d..5ed408db6 100644
--- a/integration-test/tests/email-autofill.macos.spec.js
+++ b/integration-test/tests/email-autofill.macos.spec.js
@@ -279,18 +279,16 @@ test.describe('macos', () => {
})
test.describe('matching performance', () => {
test('real-world form', async ({page}) => {
- await forwardConsoleMessages(page)
await createWebkitMocks().applyTo(page)
await defaultMacosScript(page)
const perfPage = scannerPerf(page)
perfPage.navigate('pages/usps_signup.html')
- perfPage.validateInitialScanPerf(200)
+ await perfPage.validateInitialScanPerf(200)
})
test('wall of 1000 fields with production settings', async ({page}) => {
- await forwardConsoleMessages(page)
await createWebkitMocks().applyTo(page)
await defaultMacosScript(page)
@@ -298,11 +296,10 @@ test.describe('macos', () => {
perfPage.navigate()
// In production, we expect autofill to bail on such a page
- perfPage.validateInitialScanPerf(2)
+ await perfPage.validateInitialScanPerf(10)
})
test('wall of 1000 fields with extreme settings', async ({page}) => {
- await forwardConsoleMessages(page)
await createWebkitMocks().applyTo(page)
await createAutofillScript()
.replaceAll(macosContentScopeReplacements())
@@ -317,7 +314,23 @@ test.describe('macos', () => {
const perfPage = scannerPerf(page)
perfPage.navigate()
- perfPage.validateInitialScanPerf(300)
+ await perfPage.validateInitialScanPerf(300)
+ })
+
+ // This could cause a crash or hang in certain browsers (Webkit 17)
+ test('large dom with potentially huge regex checks', async ({page}) => {
+ // If this fails, the process is expected to crash or hang. The timeout hopefully shortens the feedback loop.
+ test.setTimeout(5000)
+ await createWebkitMocks().applyTo(page)
+ await createAutofillScript()
+ .replaceAll(macosContentScopeReplacements())
+ .platform('macos')
+ .applyTo(page)
+
+ const perfPage = scannerPerf(page)
+ perfPage.navigate(constants.pages['perf-huge-regex'])
+
+ await perfPage.validateInitialScanPerf(80)
})
})
})
diff --git a/src/DeviceInterface/initFormSubmissionsApi.js b/src/DeviceInterface/initFormSubmissionsApi.js
index 8474f7082..91d2c9c03 100644
--- a/src/DeviceInterface/initFormSubmissionsApi.js
+++ b/src/DeviceInterface/initFormSubmissionsApi.js
@@ -1,4 +1,4 @@
-import {buttonMatchesFormType, getTextShallow} from '../autofill-utils.js'
+import {buttonMatchesFormType, getTextShallow, safeRegexTest} from '../autofill-utils.js'
import {extractElementStrings} from '../Form/label-util.js'
/**
@@ -51,7 +51,7 @@ export function initFormSubmissionsApi (forms, matching) {
if (!button) return
const text = getTextShallow(button) || extractElementStrings(button).join(' ')
- const hasRelevantText = matching.getDDGMatcherRegex('submitButtonRegex')?.test(text)
+ const hasRelevantText = safeRegexTest(matching.getDDGMatcherRegex('submitButtonRegex'), text)
if (hasRelevantText && text.length < 25) {
// check if there's a form with values
const filledForm = [...forms.values()].find(form => form.hasValues())
@@ -77,7 +77,7 @@ export function initFormSubmissionsApi (forms, matching) {
const entries = list.getEntries().filter((entry) =>
// @ts-ignore why does TS not know about `entry.initiatorType`?
['fetch', 'xmlhttprequest'].includes(entry.initiatorType) &&
- /login|sign-in|signin/.test(entry.name)
+ safeRegexTest(/login|sign-in|signin/, entry.name)
)
if (!entries.length) return
diff --git a/src/Form/Form.js b/src/Form/Form.js
index 41c2fad01..13a352cb7 100644
--- a/src/Form/Form.js
+++ b/src/Form/Form.js
@@ -7,7 +7,7 @@ import {
isEventWithinDax,
isLikelyASubmitButton,
isPotentiallyViewable, buttonMatchesFormType,
- safeExecute, getTextShallow, wasAutofilledByChrome, shouldLog
+ safeExecute, getTextShallow, wasAutofilledByChrome, shouldLog, safeRegexTest
} from '../autofill-utils.js'
import {getInputSubtype, getInputMainType, createMatching} from './matching.js'
@@ -207,7 +207,7 @@ class Form {
const probableField = hiddenFields.find((field) => {
const regex = new RegExp('email|' + this.matching.getDDGMatcherRegex('username')?.source)
const attributeText = field.id + ' ' + field.name
- return regex?.test(attributeText)
+ return safeRegexTest(regex, attributeText)
})
if (probableField?.value) {
formValues.credentials.username = probableField.value
diff --git a/src/Form/FormAnalyzer.js b/src/Form/FormAnalyzer.js
index c69078daf..3ef4a9483 100644
--- a/src/Form/FormAnalyzer.js
+++ b/src/Form/FormAnalyzer.js
@@ -1,7 +1,7 @@
import { removeExcessWhitespace, Matching } from './matching.js'
import { constants } from '../constants.js'
import { matchingConfiguration } from './matching-config/__generated__/compiled-matching-config.js'
-import { getTextShallow, isLikelyASubmitButton } from '../autofill-utils.js'
+import {getTextShallow, isLikelyASubmitButton, safeRegexTest} from '../autofill-utils.js'
class FormAnalyzer {
/** @type HTMLElement */
@@ -123,16 +123,23 @@ class FormAnalyzer {
string.length > constants.TEXT_LENGTH_CUTOFF
) return this
- const matchesLogin = /current.?password/i.test(string) || this.matching.getDDGMatcherRegex('loginRegex')?.test(string) || this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string)
+ const matchesLogin =
+ safeRegexTest(/current.?password/i, string) ||
+ safeRegexTest(this.matching.getDDGMatcherRegex('loginRegex'), string) ||
+ safeRegexTest(this.matching.getDDGMatcherRegex('resetPasswordLink'), string)
// Check explicitly for unified login/signup forms
- if (shouldCheckUnifiedForm && matchesLogin && this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(string)) {
+ if (
+ shouldCheckUnifiedForm &&
+ matchesLogin &&
+ safeRegexTest(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), string)
+ ) {
this.increaseHybridSignal(strength, signalType)
return this
}
const signupRegexToUse = this.matching.getDDGMatcherRegex(shouldBeConservative ? 'conservativeSignupRegex' : 'signupRegex')
- const matchesSignup = /new.?password/i.test(string) || signupRegexToUse?.test(string)
+ const matchesSignup = safeRegexTest(/new.?password/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) {
@@ -162,8 +169,8 @@ class FormAnalyzer {
evaluateUrl () {
const path = window.location.pathname
- const matchesLogin = this.matching.getDDGMatcherRegex('loginRegex')?.test(path)
- const matchesSignup = this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(path)
+ const matchesLogin = safeRegexTest(this.matching.getDDGMatcherRegex('loginRegex'), path)
+ const matchesSignup = safeRegexTest(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), path)
// If the url matches both, do nothing: the signal is probably confounding
if (matchesLogin && matchesSignup) return
@@ -253,10 +260,10 @@ class FormAnalyzer {
let shouldFlip = true
let strength = 1
// Don't flip forgotten password links
- if (this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string)) {
+ if (safeRegexTest(this.matching.getDDGMatcherRegex('resetPasswordLink'), string)) {
shouldFlip = false
strength = 3
- } else if (this.matching.getDDGMatcherRegex('loginProvidersRegex')?.test(string)) {
+ } else if (safeRegexTest(this.matching.getDDGMatcherRegex('loginProvidersRegex'), string)) {
// Don't flip login providers links
shouldFlip = false
}
@@ -323,7 +330,7 @@ class FormAnalyzer {
// Read form attributes to find a signal
const hasCCAttribute = [...formEl.attributes].some(({name, value}) =>
- /(credit|payment).?card/i.test(`${name}=${value}`)
+ safeRegexTest(/(credit|payment).?card/i, `${name}=${value}`)
)
if (hasCCAttribute) {
this._isCCForm = true
diff --git a/src/Form/matching.js b/src/Form/matching.js
index 6130af280..449cd778e 100644
--- a/src/Form/matching.js
+++ b/src/Form/matching.js
@@ -2,7 +2,7 @@ import {constants} from '../constants.js'
import {EXCLUDED_TAGS, extractElementStrings} from './label-util.js'
import {matchingConfiguration} from './matching-config/__generated__/compiled-matching-config.js'
import {logMatching, logUnmatched} from './matching-utils.js'
-import {getTextShallow} from '../autofill-utils.js'
+import {getTextShallow, safeRegexTest} from '../autofill-utils.js'
const { TEXT_LENGTH_CUTOFF, ATTR_INPUT_TYPE } = constants
@@ -463,7 +463,7 @@ class Matching {
if (!notRegex) {
return { ...result, matched: false }
}
- if (notRegex.test(elementString)) {
+ if (safeRegexTest(notRegex, elementString)) {
return { ...result, matched: false, proceed: false }
} else {
// All good here, increment the score
@@ -476,13 +476,13 @@ class Matching {
if (!skipRegex) {
return { ...result, matched: false }
}
- if (skipRegex.test(elementString)) {
+ if (safeRegexTest(skipRegex, elementString)) {
return { ...result, matched: false, skip: true }
}
}
// if the `match` regex fails, moves onto the next string
- if (!matchRexExp.test(elementString)) {
+ if (!safeRegexTest(matchRexExp, elementString)) {
continue
}
@@ -525,7 +525,7 @@ class Matching {
for (let stringName of stringsToMatch) {
let elementString = this.activeElementStrings[stringName]
if (!elementString) continue
- if (regex.test(elementString)) {
+ if (safeRegexTest(regex, elementString)) {
return {
...defaultResult,
matched: true,
diff --git a/src/autofill-utils.js b/src/autofill-utils.js
index 493f29e3b..e0a666a83 100644
--- a/src/autofill-utils.js
+++ b/src/autofill-utils.js
@@ -1,4 +1,5 @@
import {getInputSubtype, removeExcessWhitespace} from './Form/matching.js'
+import {constants} from './constants.js'
const SIGN_IN_MSG = { signMeIn: true }
@@ -315,17 +316,17 @@ const isLikelyASubmitButton = (el, matching) => {
if (
(el.getAttribute('type') === 'submit' || // is explicitly set as "submit"
el.getAttribute('name') === 'submit') && // is called "submit"
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel)
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel)
) return true
return (
- /primary|submit/i.test(el.className) || // has high-signal submit classes
- /submit/i.test(dataTestId) ||
- matching.getDDGMatcherRegex('submitButtonRegex')?.test(text) || // has high-signal text
- (el.offsetHeight * el.offsetWidth >= 10000 && !/secondary/i.test(el.className)) // it's a large element 250x40px
+ safeRegexTest(/primary|submit/i, el.className) || // has high-signal submit classes
+ safeRegexTest(/submit/i, dataTestId) ||
+ safeRegexTest(matching.getDDGMatcherRegex('submitButtonRegex'), text) || // has high-signal text
+ (el.offsetHeight * el.offsetWidth >= 10000 && !safeRegexTest(/secondary/i, el.className)) // it's a large element 250x40px
) &&
el.offsetHeight * el.offsetWidth >= 2000 && // it's not a very small button like inline links and such
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel)
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel)
}
/**
@@ -335,9 +336,9 @@ const isLikelyASubmitButton = (el, matching) => {
*/
const buttonMatchesFormType = (el, formObj) => {
if (formObj.isLogin) {
- return !/sign.?up|register|join/i.test(el.textContent || '')
+ return !safeRegexTest(/sign.?up|register|join/i, el.textContent || '')
} else if (formObj.isSignup) {
- return !/(log|sign).?([io])n/i.test(el.textContent || '')
+ return !safeRegexTest(/(log|sign).?([io])n/i, el.textContent || '')
} else {
return true
}
@@ -506,6 +507,18 @@ function isFormLikelyToBeUsedAsPageWrapper (form) {
return formChildrenPercentage > 50
}
+/**
+ * Wrapper around RegExp.test that safeguard against checking huge strings
+ * @param {RegExp | undefined} regex
+ * @param {String} string
+ * @returns {boolean}
+ */
+function safeRegexTest (regex, string) {
+ if (!string || !regex || string.length > constants.TEXT_LENGTH_CUTOFF) return false
+
+ return regex.test(string)
+}
+
export {
notifyWebApp,
sendAndWaitForAnswer,
@@ -534,5 +547,6 @@ export {
logPerformance,
whenIdle,
truncateFromMiddle,
- isFormLikelyToBeUsedAsPageWrapper
+ isFormLikelyToBeUsedAsPageWrapper,
+ safeRegexTest
}
diff --git a/swift-package/Resources/assets/autofill-debug.js b/swift-package/Resources/assets/autofill-debug.js
index 3472e1d2a..22590f9a5 100644
--- a/swift-package/Resources/assets/autofill-debug.js
+++ b/swift-package/Resources/assets/autofill-debug.js
@@ -9262,7 +9262,7 @@ function initFormSubmissionsApi(forms, matching) {
const button = /** @type HTMLElement */event.target?.closest(selector);
if (!button) return;
const text = (0, _autofillUtils.getTextShallow)(button) || (0, _labelUtil.extractElementStrings)(button).join(' ');
- const hasRelevantText = matching.getDDGMatcherRegex('submitButtonRegex')?.test(text);
+ const hasRelevantText = (0, _autofillUtils.safeRegexTest)(matching.getDDGMatcherRegex('submitButtonRegex'), text);
if (hasRelevantText && text.length < 25) {
// check if there's a form with values
const filledForm = [...forms.values()].find(form => form.hasValues());
@@ -9287,7 +9287,7 @@ function initFormSubmissionsApi(forms, matching) {
const observer = new PerformanceObserver(list => {
const entries = list.getEntries().filter(entry =>
// @ts-ignore why does TS not know about `entry.initiatorType`?
- ['fetch', 'xmlhttprequest'].includes(entry.initiatorType) && /login|sign-in|signin/.test(entry.name));
+ ['fetch', 'xmlhttprequest'].includes(entry.initiatorType) && (0, _autofillUtils.safeRegexTest)(/login|sign-in|signin/, entry.name));
if (!entries.length) return;
const filledForm = [...forms.values()].find(form => form.hasValues());
const focusedForm = [...forms.values()].find(form => form.hasFocus());
@@ -9577,7 +9577,7 @@ class Form {
const probableField = hiddenFields.find(field => {
const regex = new RegExp('email|' + this.matching.getDDGMatcherRegex('username')?.source);
const attributeText = field.id + ' ' + field.name;
- return regex?.test(attributeText);
+ return (0, _autofillUtils.safeRegexTest)(regex, attributeText);
});
if (probableField?.value) {
formValues.credentials.username = probableField.value;
@@ -10317,15 +10317,15 @@ class FormAnalyzer {
} = _ref;
// If the string is empty or too long (noisy) do nothing
if (!string || string.length > _constants.constants.TEXT_LENGTH_CUTOFF) return this;
- const matchesLogin = /current.?password/i.test(string) || this.matching.getDDGMatcherRegex('loginRegex')?.test(string) || this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string);
+ const matchesLogin = (0, _autofillUtils.safeRegexTest)(/current.?password/i, string) || (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginRegex'), string) || (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('resetPasswordLink'), string);
// Check explicitly for unified login/signup forms
- if (shouldCheckUnifiedForm && matchesLogin && this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(string)) {
+ if (shouldCheckUnifiedForm && matchesLogin && (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), string)) {
this.increaseHybridSignal(strength, signalType);
return this;
}
const signupRegexToUse = this.matching.getDDGMatcherRegex(shouldBeConservative ? 'conservativeSignupRegex' : 'signupRegex');
- const matchesSignup = /new.?password/i.test(string) || signupRegexToUse?.test(string);
+ const matchesSignup = (0, _autofillUtils.safeRegexTest)(/new.?password/i, string) || (0, _autofillUtils.safeRegexTest)(signupRegexToUse, string);
// In some cases a login match means the login is somewhere else, i.e. when a link points outside
if (shouldFlip) {
@@ -10353,8 +10353,8 @@ class FormAnalyzer {
}
evaluateUrl() {
const path = window.location.pathname;
- const matchesLogin = this.matching.getDDGMatcherRegex('loginRegex')?.test(path);
- const matchesSignup = this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(path);
+ const matchesLogin = (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginRegex'), path);
+ const matchesSignup = (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), path);
// If the url matches both, do nothing: the signal is probably confounding
if (matchesLogin && matchesSignup) return;
@@ -10439,10 +10439,10 @@ class FormAnalyzer {
let shouldFlip = true;
let strength = 1;
// Don't flip forgotten password links
- if (this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string)) {
+ if ((0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('resetPasswordLink'), string)) {
shouldFlip = false;
strength = 3;
- } else if (this.matching.getDDGMatcherRegex('loginProvidersRegex')?.test(string)) {
+ } else if ((0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginProvidersRegex'), string)) {
// Don't flip login providers links
shouldFlip = false;
}
@@ -10521,7 +10521,7 @@ class FormAnalyzer {
name,
value
} = _ref2;
- return /(credit|payment).?card/i.test(`${name}=${value}`);
+ return (0, _autofillUtils.safeRegexTest)(/(credit|payment).?card/i, `${name}=${value}`);
});
if (hasCCAttribute) {
this._isCCForm = true;
@@ -12811,7 +12811,7 @@ class Matching {
matched: false
};
}
- if (notRegex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(notRegex, elementString)) {
return {
...result,
matched: false,
@@ -12830,7 +12830,7 @@ class Matching {
matched: false
};
}
- if (skipRegex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(skipRegex, elementString)) {
return {
...result,
matched: false,
@@ -12840,7 +12840,7 @@ class Matching {
}
// if the `match` regex fails, moves onto the next string
- if (!matchRexExp.test(elementString)) {
+ if (!(0, _autofillUtils.safeRegexTest)(matchRexExp, elementString)) {
continue;
}
@@ -12891,7 +12891,7 @@ class Matching {
for (let stringName of stringsToMatch) {
let elementString = this.activeElementStrings[stringName];
if (!elementString) continue;
- if (regex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(regex, elementString)) {
return {
...defaultResult,
matched: true,
@@ -15867,13 +15867,16 @@ exports.isLocalNetwork = isLocalNetwork;
exports.isPotentiallyViewable = void 0;
exports.isValidTLD = isValidTLD;
exports.logPerformance = logPerformance;
-exports.setValue = exports.sendAndWaitForAnswer = exports.safeExecute = exports.removeInlineStyles = exports.notifyWebApp = void 0;
+exports.safeExecute = exports.removeInlineStyles = exports.notifyWebApp = void 0;
+exports.safeRegexTest = safeRegexTest;
+exports.setValue = exports.sendAndWaitForAnswer = void 0;
exports.shouldLog = shouldLog;
exports.shouldLogPerformance = shouldLogPerformance;
exports.truncateFromMiddle = truncateFromMiddle;
exports.wasAutofilledByChrome = void 0;
exports.whenIdle = whenIdle;
var _matching = require("./Form/matching.js");
+var _constants = require("./constants.js");
const SIGN_IN_MSG = exports.SIGN_IN_MSG = {
signMeIn: true
};
@@ -16226,15 +16229,15 @@ const isLikelyASubmitButton = (el, matching) => {
// is explicitly set as "submit"
el.getAttribute('name') === 'submit') &&
// is called "submit"
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel)) return true;
- return (/primary|submit/i.test(el.className) ||
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel)) return true;
+ return (safeRegexTest(/primary|submit/i, el.className) ||
// has high-signal submit classes
- /submit/i.test(dataTestId) || matching.getDDGMatcherRegex('submitButtonRegex')?.test(text) ||
+ safeRegexTest(/submit/i, dataTestId) || safeRegexTest(matching.getDDGMatcherRegex('submitButtonRegex'), text) ||
// has high-signal text
- el.offsetHeight * el.offsetWidth >= 10000 && !/secondary/i.test(el.className) // it's a large element 250x40px
+ el.offsetHeight * el.offsetWidth >= 10000 && !safeRegexTest(/secondary/i, el.className) // it's a large element 250x40px
) && el.offsetHeight * el.offsetWidth >= 2000 &&
// it's not a very small button like inline links and such
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel);
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel);
};
/**
@@ -16245,9 +16248,9 @@ const isLikelyASubmitButton = (el, matching) => {
exports.isLikelyASubmitButton = isLikelyASubmitButton;
const buttonMatchesFormType = (el, formObj) => {
if (formObj.isLogin) {
- return !/sign.?up|register|join/i.test(el.textContent || '');
+ return !safeRegexTest(/sign.?up|register|join/i, el.textContent || '');
} else if (formObj.isSignup) {
- return !/(log|sign).?([io])n/i.test(el.textContent || '');
+ return !safeRegexTest(/(log|sign).?([io])n/i, el.textContent || '');
} else {
return true;
}
@@ -16408,7 +16411,18 @@ function isFormLikelyToBeUsedAsPageWrapper(form) {
return formChildrenPercentage > 50;
}
-},{"./Form/matching.js":43}],62:[function(require,module,exports){
+/**
+ * Wrapper around RegExp.test that safeguard against checking huge strings
+ * @param {RegExp | undefined} regex
+ * @param {String} string
+ * @returns {boolean}
+ */
+function safeRegexTest(regex, string) {
+ if (!string || !regex || string.length > _constants.constants.TEXT_LENGTH_CUTOFF) return false;
+ return regex.test(string);
+}
+
+},{"./Form/matching.js":43,"./constants.js":64}],62:[function(require,module,exports){
"use strict";
require("./requestIdleCallback.js");
diff --git a/swift-package/Resources/assets/autofill.js b/swift-package/Resources/assets/autofill.js
index 8fa1a238c..bd228ac95 100644
--- a/swift-package/Resources/assets/autofill.js
+++ b/swift-package/Resources/assets/autofill.js
@@ -5317,7 +5317,7 @@ function initFormSubmissionsApi(forms, matching) {
const button = /** @type HTMLElement */event.target?.closest(selector);
if (!button) return;
const text = (0, _autofillUtils.getTextShallow)(button) || (0, _labelUtil.extractElementStrings)(button).join(' ');
- const hasRelevantText = matching.getDDGMatcherRegex('submitButtonRegex')?.test(text);
+ const hasRelevantText = (0, _autofillUtils.safeRegexTest)(matching.getDDGMatcherRegex('submitButtonRegex'), text);
if (hasRelevantText && text.length < 25) {
// check if there's a form with values
const filledForm = [...forms.values()].find(form => form.hasValues());
@@ -5342,7 +5342,7 @@ function initFormSubmissionsApi(forms, matching) {
const observer = new PerformanceObserver(list => {
const entries = list.getEntries().filter(entry =>
// @ts-ignore why does TS not know about `entry.initiatorType`?
- ['fetch', 'xmlhttprequest'].includes(entry.initiatorType) && /login|sign-in|signin/.test(entry.name));
+ ['fetch', 'xmlhttprequest'].includes(entry.initiatorType) && (0, _autofillUtils.safeRegexTest)(/login|sign-in|signin/, entry.name));
if (!entries.length) return;
const filledForm = [...forms.values()].find(form => form.hasValues());
const focusedForm = [...forms.values()].find(form => form.hasFocus());
@@ -5632,7 +5632,7 @@ class Form {
const probableField = hiddenFields.find(field => {
const regex = new RegExp('email|' + this.matching.getDDGMatcherRegex('username')?.source);
const attributeText = field.id + ' ' + field.name;
- return regex?.test(attributeText);
+ return (0, _autofillUtils.safeRegexTest)(regex, attributeText);
});
if (probableField?.value) {
formValues.credentials.username = probableField.value;
@@ -6372,15 +6372,15 @@ class FormAnalyzer {
} = _ref;
// If the string is empty or too long (noisy) do nothing
if (!string || string.length > _constants.constants.TEXT_LENGTH_CUTOFF) return this;
- const matchesLogin = /current.?password/i.test(string) || this.matching.getDDGMatcherRegex('loginRegex')?.test(string) || this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string);
+ const matchesLogin = (0, _autofillUtils.safeRegexTest)(/current.?password/i, string) || (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginRegex'), string) || (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('resetPasswordLink'), string);
// Check explicitly for unified login/signup forms
- if (shouldCheckUnifiedForm && matchesLogin && this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(string)) {
+ if (shouldCheckUnifiedForm && matchesLogin && (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), string)) {
this.increaseHybridSignal(strength, signalType);
return this;
}
const signupRegexToUse = this.matching.getDDGMatcherRegex(shouldBeConservative ? 'conservativeSignupRegex' : 'signupRegex');
- const matchesSignup = /new.?password/i.test(string) || signupRegexToUse?.test(string);
+ const matchesSignup = (0, _autofillUtils.safeRegexTest)(/new.?password/i, string) || (0, _autofillUtils.safeRegexTest)(signupRegexToUse, string);
// In some cases a login match means the login is somewhere else, i.e. when a link points outside
if (shouldFlip) {
@@ -6408,8 +6408,8 @@ class FormAnalyzer {
}
evaluateUrl() {
const path = window.location.pathname;
- const matchesLogin = this.matching.getDDGMatcherRegex('loginRegex')?.test(path);
- const matchesSignup = this.matching.getDDGMatcherRegex('conservativeSignupRegex')?.test(path);
+ const matchesLogin = (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginRegex'), path);
+ const matchesSignup = (0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('conservativeSignupRegex'), path);
// If the url matches both, do nothing: the signal is probably confounding
if (matchesLogin && matchesSignup) return;
@@ -6494,10 +6494,10 @@ class FormAnalyzer {
let shouldFlip = true;
let strength = 1;
// Don't flip forgotten password links
- if (this.matching.getDDGMatcherRegex('resetPasswordLink')?.test(string)) {
+ if ((0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('resetPasswordLink'), string)) {
shouldFlip = false;
strength = 3;
- } else if (this.matching.getDDGMatcherRegex('loginProvidersRegex')?.test(string)) {
+ } else if ((0, _autofillUtils.safeRegexTest)(this.matching.getDDGMatcherRegex('loginProvidersRegex'), string)) {
// Don't flip login providers links
shouldFlip = false;
}
@@ -6576,7 +6576,7 @@ class FormAnalyzer {
name,
value
} = _ref2;
- return /(credit|payment).?card/i.test(`${name}=${value}`);
+ return (0, _autofillUtils.safeRegexTest)(/(credit|payment).?card/i, `${name}=${value}`);
});
if (hasCCAttribute) {
this._isCCForm = true;
@@ -8866,7 +8866,7 @@ class Matching {
matched: false
};
}
- if (notRegex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(notRegex, elementString)) {
return {
...result,
matched: false,
@@ -8885,7 +8885,7 @@ class Matching {
matched: false
};
}
- if (skipRegex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(skipRegex, elementString)) {
return {
...result,
matched: false,
@@ -8895,7 +8895,7 @@ class Matching {
}
// if the `match` regex fails, moves onto the next string
- if (!matchRexExp.test(elementString)) {
+ if (!(0, _autofillUtils.safeRegexTest)(matchRexExp, elementString)) {
continue;
}
@@ -8946,7 +8946,7 @@ class Matching {
for (let stringName of stringsToMatch) {
let elementString = this.activeElementStrings[stringName];
if (!elementString) continue;
- if (regex.test(elementString)) {
+ if ((0, _autofillUtils.safeRegexTest)(regex, elementString)) {
return {
...defaultResult,
matched: true,
@@ -11922,13 +11922,16 @@ exports.isLocalNetwork = isLocalNetwork;
exports.isPotentiallyViewable = void 0;
exports.isValidTLD = isValidTLD;
exports.logPerformance = logPerformance;
-exports.setValue = exports.sendAndWaitForAnswer = exports.safeExecute = exports.removeInlineStyles = exports.notifyWebApp = void 0;
+exports.safeExecute = exports.removeInlineStyles = exports.notifyWebApp = void 0;
+exports.safeRegexTest = safeRegexTest;
+exports.setValue = exports.sendAndWaitForAnswer = void 0;
exports.shouldLog = shouldLog;
exports.shouldLogPerformance = shouldLogPerformance;
exports.truncateFromMiddle = truncateFromMiddle;
exports.wasAutofilledByChrome = void 0;
exports.whenIdle = whenIdle;
var _matching = require("./Form/matching.js");
+var _constants = require("./constants.js");
const SIGN_IN_MSG = exports.SIGN_IN_MSG = {
signMeIn: true
};
@@ -12281,15 +12284,15 @@ const isLikelyASubmitButton = (el, matching) => {
// is explicitly set as "submit"
el.getAttribute('name') === 'submit') &&
// is called "submit"
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel)) return true;
- return (/primary|submit/i.test(el.className) ||
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel)) return true;
+ return (safeRegexTest(/primary|submit/i, el.className) ||
// has high-signal submit classes
- /submit/i.test(dataTestId) || matching.getDDGMatcherRegex('submitButtonRegex')?.test(text) ||
+ safeRegexTest(/submit/i, dataTestId) || safeRegexTest(matching.getDDGMatcherRegex('submitButtonRegex'), text) ||
// has high-signal text
- el.offsetHeight * el.offsetWidth >= 10000 && !/secondary/i.test(el.className) // it's a large element 250x40px
+ el.offsetHeight * el.offsetWidth >= 10000 && !safeRegexTest(/secondary/i, el.className) // it's a large element 250x40px
) && el.offsetHeight * el.offsetWidth >= 2000 &&
// it's not a very small button like inline links and such
- !matching.getDDGMatcherRegex('submitButtonUnlikelyRegex')?.test(text + ' ' + ariaLabel);
+ !safeRegexTest(matching.getDDGMatcherRegex('submitButtonUnlikelyRegex'), text + ' ' + ariaLabel);
};
/**
@@ -12300,9 +12303,9 @@ const isLikelyASubmitButton = (el, matching) => {
exports.isLikelyASubmitButton = isLikelyASubmitButton;
const buttonMatchesFormType = (el, formObj) => {
if (formObj.isLogin) {
- return !/sign.?up|register|join/i.test(el.textContent || '');
+ return !safeRegexTest(/sign.?up|register|join/i, el.textContent || '');
} else if (formObj.isSignup) {
- return !/(log|sign).?([io])n/i.test(el.textContent || '');
+ return !safeRegexTest(/(log|sign).?([io])n/i, el.textContent || '');
} else {
return true;
}
@@ -12463,7 +12466,18 @@ function isFormLikelyToBeUsedAsPageWrapper(form) {
return formChildrenPercentage > 50;
}
-},{"./Form/matching.js":33}],52:[function(require,module,exports){
+/**
+ * Wrapper around RegExp.test that safeguard against checking huge strings
+ * @param {RegExp | undefined} regex
+ * @param {String} string
+ * @returns {boolean}
+ */
+function safeRegexTest(regex, string) {
+ if (!string || !regex || string.length > _constants.constants.TEXT_LENGTH_CUTOFF) return false;
+ return regex.test(string);
+}
+
+},{"./Form/matching.js":33,"./constants.js":54}],52:[function(require,module,exports){
"use strict";
require("./requestIdleCallback.js");