GitHub Security Advisory: GHSA-q89c-q3h5-w34g
Package: i18next-http-backend (NPM)
Severity: MODERATE (CVSS 6.5)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), CWE-74: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
Vulnerable versions: < 3.0.5
Patched in: 3.0.5

Summary:  i18next-http-backend has Path Traversal & URL Injection via Unsanitised lng/ns

Details:
### Summary

Versions of `i18next-http-backend` prior to 3.0.5 interpolate the `lng` and `ns` values directly into the configured `loadPath` / `addPath` URL template without any encoding, validation, or path sanitisation. When an application exposes the language-code selection to user-controlled input (the default — `i18next-browser-languagedetector` reads `?lng=` query params, cookies, `localStorage`, and request headers), an attacker can inject characters that change the structure of the outgoing request URL.

Affected call sites:

- `_readAny` — `lib/index.js:64`: `interpolate(resolvedLoadPath, { lng: languages.join('+'), ns: namespaces.join('+') })`
- `create` — `lib/index.js:123` (pre-patch): `interpolate(addPath, { lng, ns: namespace })`

The helper `interpolate` (`lib/utils.js`) previously returned the raw value with no encoding. In contrast, `addQueryString` already correctly uses `encodeURIComponent` for each query-string param — only the URL-path substitution was unprotected.

### Impact

An attacker who can influence the resolved `lng` or `ns` value can alter the URL in several ways:

- **Path traversal** — `lng = '../../config'` turns `/locales/{{lng}}/{{ns}}.json` into `/locales/../../config/translation.json`. On a misconfigured web server, this can cause the request to target a different resource than intended; in SSR pipelines that use `file://` or similar schemes for `loadPath`, it can read arbitrary files from the host filesystem.
- **Query-string injection** — `lng = 'en?admin=true'` turns `/locales/{{lng}}/{{ns}}.json` into `/locales/en?admin=true/translation.json`. Some server frameworks parse the query portion with higher priority than the path and branch on attacker-controlled flags.
- **Fragment truncation** — `lng = 'en#anything'` silently discards the rest of the path in browser fetches (client cannot see the final URL).
- **URL-encoded bypasses** — `lng = 'en%2F..'`, after server-side URL decoding, resolves to `en/..` — the attacker bypasses the absence of a literal `/` in their input.

The practical worst case is **SSRF** when `loadPath` is an internal or file-scheme URL, and **path-based authorisation bypass** against servers that segment access by URL prefix.

### Also fixed in 3.0.5

- **Per-instance `omitFetchOptions`.** A module-level boolean in `lib/request.js` was flipped to `true` the first time any backend instance hit a "not implemented" fetch error. Once flipped, **all** subsequent requests from **all** backend instances in the same module silently stripped every user-configured fetch option — including security-relevant `credentials`, `mode`, and `cache`. One misbehaving instance (for example during SSR hydration or in React Native) permanently removed these protections process-wide. 3.0.5 scopes the flag to the backend's `options` object (`options._omitFetchOptions`) so one instance's fallback cannot pollute siblings.
- **Log forging via control characters in `lng`/`ns`.** Error callbacks embedded the raw `