Commits
Tarun Sukhani authored 7601ef2650f
fix(security): bound the recursive message placeholder pattern (ReDoS)
Messages.formatString applies the "&{key}" pattern to the output of
String.format - i.e. after the caller's args are interpolated - so
Messages.get("greeting", userInput) puts user data in front of the regex. With
many "&{" and no closing brace, the lazy ".*?" rescanned to end-of-string from
every start: quadratic (java/polynomial-redos).
The length bound is what fixes it, not the character class. Measured over
"&{" x64000:
.*? 6330ms (original)
[^}]* 16043ms (worse - the obvious "fix")
[^}]*+ 8630ms
[^}]{0,255} 68ms
[^}]{0,255}+ 35ms (chosen)
Both unbounded alternatives are still quadratic because find() retries at every
"&{" and the quantifier rescans the remainder each time; only capping the scan
per start position makes it linear. Worth recording, since [^}]* looks like the
natural fix and is 2.5x slower than what it replaces.
A key longer than 255 chars is no longer substituted. Real message keys are far
shorter.
Tests: MessagesRedosTest covers the hostile input under a 3s budget (fixed
~0.14s, original 3.07s and timing out) and that ordinary substitution still
resolves. Verified by reverting the pattern - 1 of 2 goes red. Full ant test
passes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>