- fix(security): the nesting regexp no longer backtracks exponentially (ReDoS) on an unclosed
$t(. Its repeated group had overlapping alternatives ([^()"']+inside(?:...)*?, and inside parentheses[^()]also matched quotes), so a string containing$t(without a closing)made the engine try every way to split the text: about 2x the runtime per extra character, and a ~25 character value hungt()for minutes. With the default config this was reachable through any interpolated value (t('greet', { name: '$t(aaaaaaaaaaaaaaaaaaaaaaaa' })): escaping does not touch$,tor(, and theskipOnVariablescheck runs the regexp on the interpolated result, sonest: falsedid not help either. Every alternative now starts with a different character (plain char,",'or(), so there is only one way to match and the cost is linear. Quoted strings and one level of parentheses in nesting options keep working (#2336). The only difference: a lone"or'inside unquoted parentheses within$t(...)is no longer matched. Affects 25.3.3 to 26.4.3. Thanks to Anisetti Chaitanya Eshwar Prasad for the responsible disclosure. - fix(security):
nest()now resolves$t(...)in a single pass and no longer nests text that came out of a resolved value. Every nested lookup reset the shared nesting regexp, so the loop started over from the beginning of the string after each match. That had two effects. First, a$t(...)inside a variable that the nested lookup deliberately kept literal (defaultskipOnVariables: true) was resolved anyway by the outer loop: withouter: 'A $t(inner) B'andinner: 'Hi {{name}}',t('outer', { name: '$t(secret)' })rendered thesecrettranslation. It now staysA Hi $t(secret) B, the same as without the outer nesting; withskipOnVariables: falseit is still resolved. Second, the cost was quadratic when an unclosed$t(preceded many nestings, which matters when an app passes untrusted input as the key itself (t(userInput)): 20,000 nestings took ~1.4 s, now ~25 ms. The resolved value is now sliced into the result instead of going throughString.replace, so$&,$`,$'and$$stay literal as before (26.4.2).