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 + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+
+

+
Štvrtok, 28. september, 2023 | + Meniny má Václav
+
+
+
+ + +
+ + +
+
+
+
+
+
+

Odomknite si
celé SME.sk

+ Kúpiť predplatné +
+
+
+
+ +
+ + + +
+ + +
+
+ + + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+ + +
+
+
+ + +
+
+ +
+
+
Štátny tajomník MZV Peter Mišík +
+
+
Odomknuté do 15:38
+
+

+ Diplomat Mišík: O Ukrajine už diskutovať nemusíme. Vieme, že patrí do Únie

+
+ Rozhovor so štátnym tajomníkom ministerstva zahraničných vecí.
+ 27. sep + + 7 +
+
+
+ + + +
+ +
+ + + +
+ +
+ +
+ + Minulý rok na Európsku noc výskumníkov „deleteli“ aj lietajúce bionické roboty +
+
+
+
+
+ Treba sa báť? +

Inzercia
+ 15 h
+
+
+ +
+ + Dnes  vieme, že  medzi cárskym ortodoxným imperializmom, imperializmom Sovietskeho zväzu a dnes Ruskej federácie nie je rozdiel. +
+
+
+
+
+ Medzi cárskym ortodoxným imperializmom, imperializmom Sovietskeho zväzu a dnešnou Ruskou federáciou nie je žiadny rozdiel. +

Magda Vášáryová
27. sep69
+
+
+ +
+ + Ilustračné foto +
+
+
+
+
+ Zamestnávateľ sa už dozvie aj o rozsudku zo zahraničia. +

18 h17
+
+
+
+
+ +
+ + + +
+ +
+ + +
+
+ +
+ +
+ + Michael Gambon. +
+
+
+
+
+ Britský herec zomrel vo veku 82 rokov. +

TASR a 1 ďalší
40m5
+
+
+ +
+ + Generálny tajomník NATO Jens Stoltenberg sa na neohlásenej návšteve Kyjeva stretol s prezidentom Volodymyrom Zelenským. +
Live
+
+
+
+
Live
+ Umerov vymenoval nových námestníkov. +

a 4 ďalší
9 h60
+
+
+ +
+ + Policajní technici prehľadávajú miesto streľby v bratislavskej Dúbravke. +
Video
+
+
+
+
Video
+ V Univerzitnej nemocnici ošetrili troch zranených, dvaja zostali hospitalizovaní. +

a 2 ďalší
8 h282
+
+
+ +
+ + Neznesiteľné teplo trápi postavy filmu. +
+
+
+
+
+ Brutální vedro reflektuje pocit, v ktorom žijeme. +

26. sep4
+
+
+ + +
+ +
+ + +
+
+
+
+
+ Dozviete sa v 9 časti komiksu Posledný follower. +

Inzercia
+ 15 h
+
+
+ +
+ + Riaditeľka Bielej noci Zuzana Pacáková. +
+
+
+
+
+ Festival sa tento rok popasoval s viacerými výzvami, hovorí jeho šéfka. +

25. sep6
+
+
+ +
+ + Radosť hráčov Sniny. +
Video
+
+
+
+
Video
+ Zápas Slovnaft Cupu sledovala výborná divácka kulisa. +

2 h1
+
+
+ +
+ + Niektoré nálezy archeológov prekvapili. +
+
+
+
+
+ Prieskum počas obnovy Námestia SNP odkryl nové svedectvá. +

5 h1
+
+
+ +
+ + Namiesto centrálneho klimatického parku nájomné byty. Poslanci schválili zámer ich výstavby. +
+
+
+
+
+ Mal to byť energeticky nulový dom. +

20 h3
+
+
+
+ + + +
+ + +
+
+ + + +
+ + +
+
+ +
+ +
+ + +
+
+
+
+
+ Parlament zmenil ústavu tak, aby zahŕňala politiku rozširovania programu jadrových zbraní. +

SITA
4 h27
+
+
+ +
+ + Ilustračná snímka +
+
+
+
+
+ V desiatke najväčších hráčov pribudlo nové meno. +

26. sep14
+
+
+ +
+ + Kapitán HC Mikron Nové Zámky (23) medzi košickými protihráčmi +
+
+
+
+
+ Novozámčanov zdobí atraktívna a efektívna hra. +

3 h1
+
+
+ +
+ + Peter Schutz +
+
+
+
+
+ V roku 2023 sa vojna neskončí. +

23 h20
+
+
+ +
+ + Ján Kaľavský v zozname osôb v pátraní. +
+
+
+
+
+ Kaľavský sa skrýva v Bosne, požičiava tam bicykle. +

19 h102
+
+
+ +
+ + Vladimír Weiss ml., kapitán Slovana Bratislava. +
Video
+
+
+
+
Video
+ V Trnave prekvapili posilou na netradičný post. +

15 h66
+
+
+ +
+ + +
+
+
+
+
+ Budú si môcť v Kremli v nedeľu aspoň troška pofúkať boľačky? +

6 h18
+
+
+ +
+ + Taylor Swift si prebrala cenu za video roka za skladbu „Anti-Hero“ počas udeľovania cien MTV Video Music Awards v utorok 12. septembra. +
+
+
+
+
+ Speváčka premietne v Európe koncertný film ešte pred turné. +

22 h6
+
+
+ +
+ + Podcast Teplá vlna +
Audio
+
+
+
+
Audio
+ Vo folklórnom súbore videlo veľa toxických vplyvov. +

27. sep4
+
+
+ +
+ + Vľavo sedí obhajca Radoslav Barna, vpravo jeho klient František. +
Foto
+
+
+
+
Foto
+ Hrozilo mu doživotie, vyhol sa mu. +

20 h5
+
+
+ +
+ + +
Audio
+
+
+
+
Audio
+ Prečo premiér Ódor bije na poplach. +

a 2 ďalší
9 h8
+
+
+ +
+ + Nataša Holinová +
+
+
+
+
+ Kaľavský trestu ľahko unikne. +

19 h56
+
+
+ +
+ + Mads Mikkelsen a jeho falošný príchod do San Sebastiánu. +
+
+
+
+
+ Dánsky herec svoj príchod na festival zinscenoval. +

27. sep6
+
+
+ +
+ + Vílie kruhy v národnom parku Namib-Naukluft. +
+
+
+
+
+ Sú dve hypotézy o ich vzniku. +

23 h6
+
+
+ +
+ + Ilustračná fotografia +
+
+
+
+
+ Prečo je nutné učiť deti trpezlivosti. +

a 2 ďalší
26. sep3
+
+
+ +
+ + Toto leto opäť ukázalo, aký význam má dobre nastavené poistenie domu, bytu a domácnosti. +
+
+
+
+
+ Vyčíňanie počasia v lete znovu ukázalo, prečo je dôležité mať domáce poistenie. +

Inzercia
+ 15 h
+
+
+ + +
+ + + +
+ + +
+ + +
+ + +
+ + + +
+ + +
+
+
+
+
Zadarmo na e-mail vám pošleme prehľad správ.
+
Odber našich e-mailov si môžete kedykoľvek vypnúť.
+
+
+
+ +
+ +
+
+
+ + + + +
+ Celá ponuka newslettrov. +
+
+
+
+
+
+
+ + + +
+ + +
+
+ +
+
+ + + +
+ + +
+
+ + +
+ + +
+
+ + +
SkryťZatvoriť reklamu
+
+ + +
+
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+
+
+
+ + + + + + + + + + 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");