Skip to content

fix(compiler): prevent ReDoS in i18n placeholder name extraction#69907

Open
arshsmith1 wants to merge 1 commit into
angular:mainfrom
arshsmith1:i18n-ph-name-redos
Open

fix(compiler): prevent ReDoS in i18n placeholder name extraction#69907
arshsmith1 wants to merge 1 commit into
angular:mainfrom
arshsmith1:i18n-ph-name-redos

Conversation

@arshsmith1

Copy link
Copy Markdown
Contributor

PR Checklist

PR Type

  • Bugfix

What is the current behavior?

Issue Number: N/A

extractPlaceholderName reads the name out of a //i18n(ph="NAME") annotation on an interpolation expression with _CUSTOM_PH_EXP:

  1. the regex chains five greedy [\s\S]* segments between the short literals i18n, (, ph, = and the opening quote.
  2. when the prefix matches but the closing quote/paren is absent, a run of i18n(ph= characters can be partitioned across those wildcards in exponentially many ways, so the match backtracks catastrophically.

An interpolation expression of '//' + 'i18n(ph='.repeat(200) (about 1.6 KB) takes ~96s to extract. This runs at template-compile time and, under JIT, in the browser when the interpolation text inside an i18n element comes from an untrusted source.

What is the new behavior?

  1. the inter-token separators are only ever whitespace, so they are bound to \s* instead of [\s\S]*.
  2. the placeholder name stops at its delimiter quote via a tempered ((?:(?!\1)[\s\S])*) group, so the name scan no longer runs to end-of-input.

Recognized annotations are unchanged for valid input (the existing named-interpolation cases still pass); the same 1.6 KB input now resolves in well under a millisecond. Added a regression test covering an unterminated annotation.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

The regex that reads the name from a `//i18n(ph="NAME")` annotation on an
interpolation expression chained five greedy `[\s\S]*` segments between the
literals `i18n`, `(`, `ph`, `=` and the opening quote. When the prefix
matches but the closing quote/paren is missing, a run of `i18n(ph=` text can be
split across those wildcards in many ways, so the match backtracks
catastrophically (`'//' + 'i18n(ph='.repeat(200)`, ~1.6 KB, takes ~96s).

The separators in that annotation are only ever whitespace, so bind them to
`\s*` and stop the name at the delimiter quote. Valid annotations are still
recognized identically; the match is now linear.
@pullapprove
pullapprove Bot requested a review from JoostK July 23, 2026 09:41
@angular-robot angular-robot Bot added the area: compiler Issues related to `ngc`, Angular's template compiler label Jul 23, 2026
@ngbot ngbot Bot added this to the Backlog milestone Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: compiler Issues related to `ngc`, Angular's template compiler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant