Skip to content

chore(deps): bump i18next from 26.4.2 to 26.4.4 - #32

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/npm_and_yarn/i18next-26.4.4
Closed

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/npm_and_yarn/i18next-26.4.4

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Oct 16, 2026

Copy link
Copy Markdown
Contributor

Bumps i18next from 26.4.2 to 26.4.4.

Release notes

Sourced from i18next's releases.

v26.4.4

  • 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 hung t() for minutes. With the default config this was reachable through any interpolated value (t('greet', { name: '$t(aaaaaaaaaaaaaaaaaaaaaaaa' })): escaping does not touch $, t or (, and the skipOnVariables check runs the regexp on the interpolated result, so nest: false did 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 (default skipOnVariables: true) was resolved anyway by the outer loop: with outer: 'A $t(inner) B' and inner: 'Hi {{name}}', t('outer', { name: '$t(secret)' }) rendered the secret translation. It now stays A Hi $t(secret) B, the same as without the outer nesting; with skipOnVariables: false it 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 through String.replace, so $&, $`, $' and $$ stay literal as before (26.4.2).

v26.4.3

  • perf: ResourceStore.hasLanguageSomeTranslations() no longer enumerates every key of a large bundle on each call. It ran Object.keys() on the whole bundle just to learn whether it had at least one key, and init() / changeLanguage() reach it three times per instance (once while picking the language, twice via setResolvedLanguage()). With large flat bundles and one i18next instance per SSR request reusing the same resources, that showed up as measurable CPU work before any t() call. Bundle objects already found non-empty are now remembered in a WeakSet shared across instances, so later checks are O(1): 2,000 inits with a 6,800-key bundle went from ~750 ms to ~20 ms, the same as a one-key bundle. Only the non-empty result is cached. No API removes single keys from a bundle, so a non-empty bundle stays non-empty, and empty bundles are re-checked every time, so keys added later (backend loads, addResource, addResourceBundle, direct or silent changes) count right away. The only edge case: a bundle emptied by hand with delete on every key while it is still in the store keeps counting as having translations; use removeResourceBundle() for that. Thanks @​dmchoi77 for the detailed report (#2449).
Changelog

Sourced from i18next's changelog.

26.4.4

  • 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 hung t() for minutes. With the default config this was reachable through any interpolated value (t('greet', { name: '$t(aaaaaaaaaaaaaaaaaaaaaaaa' })): escaping does not touch $, t or (, and the skipOnVariables check runs the regexp on the interpolated result, so nest: false did 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. See advisory GHSA-5jr9-983r-c8x4, CVSS 7.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H). 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 (default skipOnVariables: true) was resolved anyway by the outer loop: with outer: 'A $t(inner) B' and inner: 'Hi {{name}}', t('outer', { name: '$t(secret)' }) rendered the secret translation. It now stays A Hi $t(secret) B, the same as without the outer nesting; with skipOnVariables: false it 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 through String.replace, so $&, $`, $' and $$ stay literal as before (26.4.2).

26.4.3

  • perf: ResourceStore.hasLanguageSomeTranslations() no longer enumerates every key of a large bundle on each call. It ran Object.keys() on the whole bundle just to learn whether it had at least one key, and init() / changeLanguage() reach it three times per instance (once while picking the language, twice via setResolvedLanguage()). With large flat bundles and one i18next instance per SSR request reusing the same resources, that showed up as measurable CPU work before any t() call. Bundle objects already found non-empty are now remembered in a WeakSet shared across instances, so later checks are O(1): 2,000 inits with a 6,800-key bundle went from ~750 ms to ~20 ms, the same as a one-key bundle. Only the non-empty result is cached. No API removes single keys from a bundle, so a non-empty bundle stays non-empty, and empty bundles are re-checked every time, so keys added later (backend loads, addResource, addResourceBundle, direct or silent changes) count right away. The only edge case: a bundle emptied by hand with delete on every key while it is still in the store keeps counting as having translations; use removeResourceBundle() for that. Thanks @​dmchoi77 for the detailed report (#2449).
Commits
  • f6a7746 26.4.4
  • 25988d5 build
  • c9f8eba fix(security): nesting regexp ReDoS on unclosed $t( and single-pass nest(), c...
  • 0d2845a 26.4.3
  • a2dbe15 build
  • e75843f perf: cache non-empty bundles in hasLanguageSomeTranslations, changelog v26.4.3
  • edaea71 Fix TypeScript examples build and update them to i18next v26 (#2448)
  • 4ebd19d docs: "For AI assistants" paragraph in the README and a fuller saveMissing JSDoc
  • See full diff in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [i18next](https://github.com/i18next/i18next) from 26.4.2 to 26.4.4.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](i18next/i18next@v26.4.2...v26.4.4)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 26.4.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot @github

dependabot Bot commented on behalf of github Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Labels

The following labels could not be found: dependencies. Please create it before Dependabot can add it to a pull request.

Please fix the above issues or remove invalid values from dependabot.yml.

@dependabot @github

dependabot Bot commented on behalf of github Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #34.

@dependabot dependabot Bot closed this Oct 19, 2026
@dependabot
dependabot Bot deleted the dependabot/npm_and_yarn/i18next-26.4.4 branch October 19, 2026 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants