Skip to content

fix(deps): update dependency i18next-fs-backend to v2 [security] - #202

Open
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/npm-i18next-fs-backend-vulnerability
Open

fix(deps): update dependency i18next-fs-backend to v2 [security]#202
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/npm-i18next-fs-backend-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Aug 30, 2026

Copy link
Copy Markdown

This PR contains the following updates:

Package Change Age Confidence
i18next-fs-backend 1.0.72.6.6 age confidence

i18next-fs-backend: Path traversal via unsanitised lng/ns allows arbitrary file read/overwrite

CVE-2026-41693 / GHSA-8847-338w-5hcj

More information

Details

Summary

Versions of i18next-fs-backend prior to 2.6.4 interpolate the caller-supplied lng and ns values directly into the configured loadPath and addPath templates with no path-component validation and no sanitisation. When an application exposes the resolved language code to user-controlled input (?lng= query parameter, cookie, request header), a crafted value can break out of the intended locale directory.

Affected call sites in lib/index.js:

  • read (line 38 pre-patch): const filename = interpolate(loadPath, { lng: language, ns: namespace })
  • removeFile (line 101 pre-patch): same pattern against addPath
  • writeFile (line 127 pre-patch): same pattern against addPath for queued missing-key writes

The helper interpolate in lib/utils.js substitutes raw values with no encoding — unlike the addQueryString helper in i18next-http-backend, there is no equivalent safety for path interpolation.

Impact
  • Arbitrary file read. With a loadPath like /locales//.json, an attacker-controlled lng = '../../etc' (and matching ns) causes the backend to read a file outside the locale directory. For parsers that tolerate arbitrary content (YAML's freeform text), the file contents surface as a translation resource.
  • Arbitrary file overwrite. addPath is interpolated the same way for missing-key writes (the create() code path and the debounced writer in writeFile). A traversing lng/ns combination can cause the process to write JSON structures to an unintended filesystem location, potentially overwriting application files if the process user has write access.
  • Chain with .js/.ts eval. i18next-fs-backend supports loading .js and .ts locale files by eval-ing their content (intentional feature, documented as requiring trusted sources). Combining traversal with that path — for example lng = '../../../app/config' against loadPath: '/locales//.js' — would cause the backend to execute a server-side file as JavaScript, exfiltrating whatever it can touch (process.env, connected services).

Exploitation requires the application to pass an untrusted lng/ns value through to i18next.t() without its own validation. Many i18next setups do exactly this via i18next-browser-languagedetector (query string / cookie detection).

Affected versions

All versions of i18next-fs-backend prior to 2.6.4.

Patch

Fixed in 2.6.4. lib/utils.js now exports:

  • isSafePathSegment(v) — returns true only if v is a non-empty string of ≤ 128 chars that does not contain .., /, \, control characters, or a prototype key (__proto__, constructor, prototype). Legitimate i18next language-code shapes (BCP-47, en_US, zh-Hant-HK, pirate-speak, my-custom.ns, +-joined multi-language values) all pass.
  • interpolatePath(template, data) — substitutes variables like the existing interpolate but refuses the whole result if any segment fails isSafePathSegment. Callers bail out with an error (read) or silently drop the queued write (writeFile, removeFile).

The .js / .ts eval behaviour is intentionally retained — dynamic expressions in locale files are a documented feature of this backend, and safe replacements like dynamic import() are async-only and incompatible with this backend's sync-capable code path. The README has a new "Security considerations" section that spells out the trust model: .js/.ts locale files must be treated as code.

Workarounds

No workaround short of upgrading. If you cannot upgrade immediately, sanitise lng / ns at your application boundary before passing them to i18next — reject values containing .., /, \, control characters, and cap the length.

Credits

Discovered via an internal security audit of the i18next ecosystem.

Severity

  • CVSS Score: 8.2 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


i18next-fs-backend vulnerable to prototype pollution via crafted missing-key string

CVE-2026-48713 / GHSA-2933-q333-qg83

More information

Details

Impact

i18next-fs-backend ≤ 2.6.5, when used to persist missing translation keys (e.g. via i18next-http-middleware's missingKeyHandler exposed to untrusted input), is vulnerable to prototype pollution via crafted missing-key strings.

Backend.writeFile() splits each queued missing-key string on the configured keySeparator (default .) before calling the internal setPath() walker. The walker (getLastOfPath in lib/utils.js) did not guard against unsafe segments, so a key like "__proto__.polluted" was split into ["__proto__", "polluted"] and walked straight into Object.prototype, allowing an attacker to write arbitrary properties onto the global object prototype.

Depending on the host application, polluted prototype properties may cause crashes, corrupted translation behaviour, configuration poisoning, or bypasses of property-based security checks.

Affected configuration

Applications are directly affected only if all of the following hold:

  • i18next-fs-backend ≤ 2.6.5 is configured as the backend.
  • i18next-http-middleware's missingKeyHandler (or another route that forwards untrusted request bodies to i18next.t(..., { ... }) with saveMissing: true) is reachable by untrusted users.
  • The default behaviour of splitting missing-key strings on keySeparator is in use (i.e. keySeparator is not false).

Apps that do not expose missing-key persistence to untrusted input are not directly affected through this attack path.

Patches

Fixed in i18next-fs-backend 2.6.6. The traversal helper now refuses to descend through __proto__, constructor, or prototype segments and drops the offending write silently. Legitimate dotted keys (e.g. "header.title") are unaffected.

A matching defence-in-depth fix has been shipped in i18next-http-middleware 3.9.7 — see the companion advisory.

Workarounds

If users cannot upgrade immediately:

  • Do not expose i18next-http-middleware's missingKeyHandler to untrusted users (mount it behind authentication, or remove the route).
  • Disable missing-key persistence (saveMissing: false, or no backend.create implementation) when accepting writes from untrusted input.
  • Set keySeparator: false in the i18next options to disable backend key splitting (note: this also disables nested translation keys).
Resources

Severity

  • CVSS Score: 9.1 / 10 (Critical)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

i18next/i18next-fs-backend (i18next-fs-backend)

v2.6.6

Compare Source

Security release — coordinated disclosure from @​codeswhite. See published advisory GHSA-2933-q333-qg83.

  • security: guard the in-memory setPath / pushPath traversal (utils.getLastOfPath) against prototype pollution via crafted missing-key strings. 2.6.4 sanitised lng/ns interpolation into filesystem paths, but did not cover the JSON-object walk that writeFile() performs on each queued missing-key entry: with the default keySeparator: '.', a key like __proto__.polluted was split into ['__proto__','polluted'] and walked straight into Object.prototype. The traversal helper now refuses to descend through __proto__, constructor, or prototype segments and drops the offending write silently; legitimate dotted keys (header.title) are unaffected. Reachable in practice via i18next-http-middleware's missingKeyHandler when exposed to untrusted input — see also the matching defence-in-depth fix in i18next-http-middleware 3.9.7. Credit: @​codeswhite (GHSA-2933-q333-qg83).

v2.6.5

Compare Source

  • fix: allow forward slashes in ns values so nested namespace names (mapping to subfolder locale files such as public/locales/en/a/b.json) load correctly again. 2.6.4's security fix applied the same strict path-segment check to both lng and ns, which was correct for lng (no BCP-47 shape contains /) but over-strict for ns — nested namespaces containing / were never officially supported, but the behaviour fell out of the implicit string-substitution semantics of loadPath and is common enough in the wild to be worth accommodating. isSafePathSegment is now split into isSafeLangSegment (strict — still rejects /) and isSafeNsSegment (loose — allows / but still rejects .., \, control chars, prototype keys, and oversized inputs). isSafePathSegment is kept as a backwards-compatible alias for the strict check. The 2.6.4 security fix remains in force for every concrete attack pattern from the original advisory. Fixes #​74.

v2.6.4

Compare Source

Security release — all issues found via an internal audit. See published advisory GHSA-8847-338w-5hcj.

  • security: refuse to build filesystem paths when lng or ns values contain .., path separators (/, \), control characters, prototype keys (__proto__ / constructor / prototype), or exceed 128 chars. Prevents arbitrary filesystem read / write via attacker-controlled language-code values. Any legitimate i18next language-code shape (BCP-47-like, underscores, hyphens, dots, +-joined multi-language requests) is still accepted (GHSA-8847-338w-5hcj)
  • docs: new "Security considerations" README section — documents the filesystem-path sanitiser and clarifies the trust model around .js/.ts locale files (their content is eval-ed, so they must be treated as code). The eval behaviour itself is retained: dynamic expressions in .js/.ts locale files are an intentional feature, and safe replacements like import() are async-only and not viable for this sync-capable code path.
  • chore: ignore .env* and *.pem/*.key files in .gitignore.

v2.6.3

Compare Source

  • use own interpolation function instead of relying on i18next's interpolator

v2.6.2

Compare Source

v2.6.1

Compare Source

  • Bump js-yaml from 4.1.0 to 4.1.1 (#​64)

v2.6.0

Compare Source

v2.5.0

Compare Source

v2.4.0

Compare Source

v2.3.2

Compare Source

v2.3.1

Compare Source

v2.3.0

Compare Source

v2.2.0

Compare Source

v2.1.5

Compare Source

v2.1.4

Compare Source

v2.1.3

Compare Source

v2.1.2

Compare Source

v2.1.1

Compare Source

v2.1.0

Compare Source

v2.0.1

Compare Source

v2.0.0

Compare Source

v1.2.0

Compare Source

v1.1.5

Compare Source

v1.1.4

Compare Source

v1.1.3

Compare Source

v1.1.2

Compare Source

v1.1.1

Compare Source

v1.1.0

Compare Source

v1.0.8

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot added the Type: Chore label Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants