i18next-http-middleware 3.9.7 → 3.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -93,7 +93,7 @@ var LanguageDetector = function () {
93
93
  return d !== undefined && d !== null && !utils.hasXSS(d);
94
94
  }).map(function (d) {
95
95
  return _this.options.convertDetectedLanguage(d);
96
- });
96
+ }).filter(utils.isSafeLangIdentifier);
97
97
  if (_this.services.languageUtils.getBestMatchFromCodes) {
98
98
  found = _this.services.languageUtils.getBestMatchFromCodes(detections);
99
99
  if (found) {
package/cjs/index.js CHANGED
@@ -280,6 +280,10 @@ function missingKeyHandler(i18next) {
280
280
  res = _checkForCombinedReqR3.res;
281
281
  var lng = options.getParams(req)[options.lngParam || 'lng'];
282
282
  var ns = options.getParams(req)[options.nsParam || 'ns'];
283
+ if (!utils.isSafeLangIdentifier(lng) || !utils.isSafeNsIdentifier(ns)) {
284
+ options.setStatus(res, 400);
285
+ return options.send(res, 'i18next-http-middleware:: unsafe lng/ns value');
286
+ }
283
287
  if (!i18next.services.backendConnector) {
284
288
  options.setStatus(res, 404);
285
289
  return options.send(res, 'i18next-express-middleware:: no backend configured');
@@ -85,7 +85,7 @@ var LanguageDetector = function () {
85
85
  return d !== undefined && d !== null && !utils.hasXSS(d);
86
86
  }).map(function (d) {
87
87
  return _this.options.convertDetectedLanguage(d);
88
- });
88
+ }).filter(utils.isSafeLangIdentifier);
89
89
  if (_this.services.languageUtils.getBestMatchFromCodes) {
90
90
  found = _this.services.languageUtils.getBestMatchFromCodes(detections);
91
91
  if (found) {
package/esm/index.js CHANGED
@@ -263,6 +263,10 @@ export function missingKeyHandler(i18next) {
263
263
  res = _checkForCombinedReqR3.res;
264
264
  var lng = options.getParams(req)[options.lngParam || 'lng'];
265
265
  var ns = options.getParams(req)[options.nsParam || 'ns'];
266
+ if (!utils.isSafeLangIdentifier(lng) || !utils.isSafeNsIdentifier(ns)) {
267
+ options.setStatus(res, 400);
268
+ return options.send(res, 'i18next-http-middleware:: unsafe lng/ns value');
269
+ }
266
270
  if (!i18next.services.backendConnector) {
267
271
  options.setStatus(res, 404);
268
272
  return options.send(res, 'i18next-express-middleware:: no backend configured');
@@ -81,9 +81,18 @@ class LanguageDetector {
81
81
  if (!detections) return
82
82
  if (!Array.isArray(detections)) detections = [detections]
83
83
 
84
+ // Detected languages come from untrusted request input (querystring,
85
+ // path, cookie, session, header) and are forwarded to
86
+ // changeLanguage()/loadLanguages(), which hand them to the backend as
87
+ // a `{{lng}}` path/URL segment. hasXSS only covers markup payloads, so
88
+ // the identifier check is what blocks directory escape (`..`, `/`,
89
+ // `\`), control characters and prototype keys. Applied *after*
90
+ // convertDetectedLanguage so a custom converter cannot reintroduce an
91
+ // unsafe value; non-string converter output is dropped (fail-closed).
84
92
  detections = detections
85
93
  .filter((d) => d !== undefined && d !== null && !utils.hasXSS(d))
86
94
  .map((d) => this.options.convertDetectedLanguage(d))
95
+ .filter(utils.isSafeLangIdentifier)
87
96
 
88
97
  if (this.services.languageUtils.getBestMatchFromCodes) { // new i18next v19.5.0
89
98
  found = this.services.languageUtils.getBestMatchFromCodes(detections)
package/lib/index.js CHANGED
@@ -302,6 +302,19 @@ export function missingKeyHandler (i18next, options = {}) {
302
302
  const lng = options.getParams(req)[options.lngParam || 'lng']
303
303
  const ns = options.getParams(req)[options.nsParam || 'ns']
304
304
 
305
+ // Same check getResourcesHandler applies, but this is the write path:
306
+ // these params are forwarded to backendConnector.saveMissing() and end
307
+ // up as the `{{lng}}`/`{{ns}}` segments of the backend's addPath, so an
308
+ // unvalidated value is an arbitrary file write on i18next-fs-backend
309
+ // (route params arrive percent-decoded, e.g. `..%2f..%2ftmp`).
310
+ if (!utils.isSafeLangIdentifier(lng) || !utils.isSafeNsIdentifier(ns)) {
311
+ options.setStatus(res, 400)
312
+ return options.send(
313
+ res,
314
+ 'i18next-http-middleware:: unsafe lng/ns value'
315
+ )
316
+ }
317
+
305
318
  if (!i18next.services.backendConnector) {
306
319
  options.setStatus(res, 404)
307
320
  return options.send(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-http-middleware",
3
- "version": "3.9.7",
3
+ "version": "3.9.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./cjs/index.js",
@@ -29,7 +29,7 @@
29
29
  "module": "./esm/index.js",
30
30
  "devDependencies": {
31
31
  "@babel/cli": "7.28.6",
32
- "@babel/core": "7.29.0",
32
+ "@babel/core": "7.29.6",
33
33
  "@babel/preset-env": "7.29.2",
34
34
  "@hapi/hapi": "^21.4.8",
35
35
  "@opentelemetry/api": "^1.9.1",
@@ -66,7 +66,7 @@
66
66
  "homepage": "https://github.com/i18next/i18next-http-middleware",
67
67
  "repository": {
68
68
  "type": "git",
69
- "url": "git@github.com:i18next/i18next-http-middleware.git"
69
+ "url": "https://github.com/i18next/i18next-http-middleware.git"
70
70
  },
71
71
  "bugs": {
72
72
  "url": "https://github.com/i18next/i18next-http-middleware/issues"