@@ -29,8 +29,10 @@ import { FluentType, FluentNone, FluentNumber, FluentDateTime }
2929 from "./types.js" ;
3030import * as builtins from "./builtins.js" ;
3131
32- // Prevent expansion of too long placeables.
33- const MAX_PLACEABLE_LENGTH = 2500 ;
32+ // The maximum number of placeables which can be expanded in a single call to
33+ // `formatPattern`. The limit protects against the Billion Laughs and Quadratic
34+ // Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx.
35+ const MAX_PLACEABLES = 100 ;
3436
3537// Unicode bidi isolation characters.
3638const FSI = "\u2068" ;
@@ -259,25 +261,24 @@ export function resolveComplexPattern(scope, ptn) {
259261 continue ;
260262 }
261263
262- const part = resolveExpression ( scope , elem ) . toString ( scope ) ;
263-
264- if ( useIsolating ) {
265- result . push ( FSI ) ;
266- }
267-
268- if ( part . length > MAX_PLACEABLE_LENGTH ) {
264+ scope . placeables ++ ;
265+ if ( scope . placeables > MAX_PLACEABLES ) {
269266 scope . dirty . delete ( ptn ) ;
270267 // This is a fatal error which causes the resolver to instantly bail out
271268 // on this pattern. The length check protects against excessive memory
272269 // usage, and throwing protects against eating up the CPU when long
273270 // placeables are deeply nested.
274271 throw new RangeError (
275- " Too many characters in placeable " +
276- `( ${ part . length } , max allowed is ${ MAX_PLACEABLE_LENGTH } ) `
272+ ` Too many placeables expanded: ${ scope . placeables } , ` +
273+ `max allowed is ${ MAX_PLACEABLES } `
277274 ) ;
278275 }
279276
280- result . push ( part ) ;
277+ if ( useIsolating ) {
278+ result . push ( FSI ) ;
279+ }
280+
281+ result . push ( resolveExpression ( scope , elem ) . toString ( scope ) ) ;
281282
282283 if ( useIsolating ) {
283284 result . push ( PDI ) ;
0 commit comments