CVE Patch Review

CVE-2026-48713: Root-Cause Fix for Prototype Pollution in i18next-fs-backend

CVE-2026-48713 · GHSA-2933-Q333-QG83 · Updated 2026-06-25 Root-cause

Summary

The 2.6.6 patch addresses the vulnerable object-path traversal used when persisting missing translation keys. The fix blocks traversal through __proto__, constructor, and prototype segments inside the in-memory setPath/pushPath helper path walk, which is the direct source of prototype pollution. Tests cover both direct helper use and the writeFile-style split of attacker-controlled dotted keys. Based on the supplied diff and advisory context, this is a root-cause remediation for the identified pollution primitive in i18next-fs-backend.

Analysis

Vulnerability

CVE-2026-48713 describes unauthenticated prototype pollution in i18next-fs-backend before 2.6.6. The vulnerable path is not the filesystem path interpolation itself, but the in-memory object traversal performed while writing queued missing translation keys. As documented in the patch changelog, when the default keySeparator: '.' is used, an attacker-controlled missing key such as __proto__.polluted is split into path segments and walked by the helper used by writeFile(), allowing descent into Object.prototype. The advisory context notes practical reachability through middleware missing-key handling when untrusted input can influence missing translation keys; see the vendor advisory and CVE records at GHSA-2933-Q333-QG83, NVD, and CVE.org.

The supplied commit summary explicitly distinguishes this issue from the earlier 2.6.4 sanitization of lng/ns path interpolation. That earlier hardening did not protect the JSON-object walk used for missing-key persistence, so crafted dotted keys still reached prototype-bearing properties. This makes the root cause a missing denylist on object path traversal, not merely unsafe file path construction.

// old behavior conceptually allowed traversal like this:
const path = '__proto__.polluted'.split('.')
setPath(data, path, 'PWNED')
// result: Object.prototype.polluted could be assigned

Patch

The 2.6.6 patch in commit 3ab0448087da6935a40117f904b7457281f963f4 modifies the traversal helper in lib/utils.js so it refuses to descend through unsafe path segments. The diff shows explicit checks for __proto__, constructor, and prototype both during traversal and for the final key segment. When an unsafe segment is encountered, the helper returns an empty result and callers drop the write silently.

// guard against prototype pollution — refuse to traverse __proto__,
// constructor or prototype segments. Returning an empty result lets
// callers drop the write silently rather than walking into Object.prototype.
if (UNSAFE_KEYS.indexOf(key) > -1) return {}
const k = cleanKey(stack.shift())
if (UNSAFE_KEYS.indexOf(k) > -1) return {}
return { obj: object, k }
if (obj === undefined) return // unsafe path — drop silently

The test additions in test/security.js are strong and directly aligned with the exploit mechanics. They verify that setPath and pushPath reject traversal through __proto__, constructor.prototype, and unsafe final segments; they also verify that legitimate nested writes still succeed. Importantly, the tests include an end-to-end simulation of the vulnerable flow where an attacker key is split on . before being passed to setPath. This is good evidence that the patch targets the actual sink rather than only a narrow input form.

Review

Pros

  • The patch addresses the direct sink: object-path traversal in the helper used by missing-key persistence, which is the mechanism described in the changelog and advisory references.
  • The denylist covers the canonical prototype-pollution segments in JavaScript object traversal: __proto__, constructor, and prototype.
  • Checks are applied both while descending the path and on the final assignment key, preventing both intermediate traversal and terminal writes to dangerous properties.
  • Regression tests are concrete, exploit-oriented, and include both helper-level and writeFile-style path splitting scenarios.
  • The patch preserves expected behavior for benign dotted keys such as header.title and normal nested translation writes.

Cons

  • The implementation drops unsafe writes silently. That avoids breakage, but it may reduce operator visibility into active exploitation attempts or malformed client behavior.
  • The reviewable diff excerpt does not show whether all helper consumers consistently handle the empty-result contract; the snippet indicates callers now return early on obj === undefined, but a full-tree audit would still be prudent.
  • The fix is denylist-based. While the listed keys are the standard high-risk prototype pollution vectors, denylist approaches are generally less future-proof than constructing null-prototype containers or enforcing a strict allowlist for path segments where feasible.

Verdict

Root-cause.

Based on the supplied commit, changelog text, and tests, the patch fixes the vulnerable object traversal primitive itself rather than merely filtering one entry point. The exploit path described by the vulnerability is that attacker-controlled missing keys are split into segments and passed into setPath/pushPath; the patch hardens that shared traversal logic against prototype-bearing segments and validates the behavior with targeted regression tests. That is consistent with a root-cause remediation for CVE-2026-48713 as documented in the fixing commit, the vendor advisory, and the NVD record.

Sources