The Translation Backend That Became an Attack Vector
When applications internationalize, they trust their translation backend to safely handle missing keys. In i18next-fs-backend 2.6.4, that trust was misplaced: a crafted string passed as a missing translation key could walk straight up to Object.prototype and rewrite it. The result? Application-wide compromise from a single malicious translation lookup.
This vulnerability, tracked as CVE-2026-48713, demonstrates how even infrastructure libraries—those that merely load JSON files from disk—can become critical security boundaries when they process attacker-influenced input without defensive validation.
Affected Versions
| Affected | <= 2.6.4 |
| Fixed in | 2.6.6 |
| Ecosystem | npm |
| CVE / GHSA | CVE-2026-48713 / not assigned |
| CWE | unknown |
The Vulnerability Explained
i18next-fs-backend provides filesystem-based translation loading for the i18next internationalization framework. When a translation key is missing, the backend attempts to resolve it through its key-path parsing logic. The vulnerability exists in how this parsing handles special property names.
The problematic pattern involves the backend's internal key resolution, where a string like __proto__.polluted or constructor.prototype.polluted would be split and used to set properties on nested objects. Without proper sanitization, these keys don't create nested translation objects—they traverse into Object.prototype itself.
Consider how i18next-fs-backend processes a missing key lookup. When i18next.t('__proto__.isAdmin') is called without that key existing, the backend's resolution path attempts to build the nested structure. The code effectively executes:
// Simplified representation of the vulnerable path
let target = {};
let parts = key.split('.');
// Iteratively: target = target[parts[i]] || {};
// When parts[0] is '__proto__', this becomes:
target['__proto__'] = target['__proto__'] || {};
Once Object.prototype.isAdmin is set to true, every object in the JavaScript runtime inherits this property. Authentication checks, authorization logic, and business rules throughout the application become compromised.
Attack Scenario
An application using i18next with browser language detection might process a URL like:
https://app.example.com/?lng=__proto__.constructor&ns=prototype&key=exec
If this flows into a translation lookup that reaches i18next-fs-backend's missing-key handler, the attacker can pollute Object.prototype with arbitrary properties. In a typical Express.js application, this could alter req.user checks, bypass Object.hasOwnProperty validations, or modify behavior in completely unrelated modules loaded later in the same process.
The Fix
The resolution patches i18next-fs-backend from version 2.6.4 to 2.6.6. The version bump includes hardened key-path parsing that blocks prototype pollution vectors:
- "i18next-fs-backend": "^2.6.4",
+ "i18next-fs-backend": "^2.6.6",
The 2.6.6 release introduces validation that rejects or escapes keys containing __proto__, constructor, and prototype as path segments. This is implemented through a deny-list approach at the point where the key string is tokenized and before any property assignment occurs.
The fix preserves all legitimate translation key patterns while eliminating the dangerous property-access chains. Applications using nested keys like common.buttons.submit continue to work; only the specifically dangerous prototype-pollution payload patterns are neutralized.
Key Takeaways
-
Translation keys are untrusted input: Any string that reaches
i18next.t()or its backend equivalents—from user URLs, API responses, or database entries—must be treated as potentially malicious. -
Missing-key handlers are attack surfaces: The code path that executes when a translation doesn't exist is often less scrutinized than the success path, making it attractive for vulnerability research.
-
Prototype pollution travels far: A compromise in i18next-fs-backend doesn't just affect translations; it poisons the entire JavaScript execution environment, including code that never imports i18next directly.
-
Patch version increments can be critical: The jump from 2.6.4 to 2.6.6 is a patch-level change, yet it addresses a critical vulnerability. Automated dependency update tools that ignore patch bumps risk missing security fixes.
-
Filesystem backends still process strings: Don't assume "just loading JSON from disk" is safe. The path from filename to loaded object involves string processing that can introduce injection vulnerabilities.
How Orbis AppSec Detected This
Orbis AppSec's static analysis traced this vulnerability through the following data flow:
- Source: User-influenced input entering through HTTP request parameters that feed into
i18next.t()translation key arguments - Sink: The
i18next-fs-backendmissing-key resolution logic that performs property assignment using dynamic key strings - Missing control: Absence of prototype pollution validation before using split key segments as property names
- CWE: unknown
- Fix: Upgraded i18next-fs-backend to 2.6.6, which implements deny-list validation for
__proto__,constructor, andprototypein key-path parsing
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
CVE-2026-48713 reminds us that prototype pollution isn't limited to deep-merge utilities and configuration parsers. Any code that splits strings and uses the results to access object properties—yes, even your translation backend—must defend against __proto__ and constructor traversal. The 2.6.6 patch closes this specific path in i18next-fs-backend, but the broader lesson applies wherever dynamic property access meets external input.