i18next-http-middleware 3.9.6 → 3.9.7

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.
package/README.md CHANGED
@@ -510,7 +510,7 @@ Don't forget: You have to add the name of your detector (`myDetectorsName` in th
510
510
  <h3 align='center'>Gold Sponsors</h3>
511
511
 
512
512
  <p align='center'>
513
- <a href='https://locize.com/' target='_blank'>
513
+ <a href='https://www.locize.com/?utm_source=i18next_http_middleware_readme&utm_medium=github&utm_campaign=readme' target='_blank'>
514
514
  <img src='https://raw.githubusercontent.com/i18next/i18next/master/assets/locize_sponsor_240.gif' width='240px'>
515
515
  </a>
516
516
  </p>
package/cjs/index.js CHANGED
@@ -285,11 +285,12 @@ function missingKeyHandler(i18next) {
285
285
  return options.send(res, 'i18next-express-middleware:: no backend configured');
286
286
  }
287
287
  var body = options.getBody(req);
288
+ var keySeparator = i18next.options && i18next.options.keySeparator;
288
289
  var saveMissingKeys = function saveMissingKeys(src) {
289
290
  if (!src || _typeof(src) !== 'object') return;
290
291
  for (var _i = 0, _Object$keys = Object.keys(src); _i < _Object$keys.length; _i++) {
291
292
  var m = _Object$keys[_i];
292
- if (utils.UNSAFE_KEYS.indexOf(m) > -1) continue;
293
+ if (utils.hasUnsafeKeySegment(m, keySeparator)) continue;
293
294
  i18next.services.backendConnector.saveMissing([lng], ns, m, src[m]);
294
295
  }
295
296
  };
package/cjs/utils.js CHANGED
@@ -7,6 +7,7 @@ exports.UNSAFE_KEYS = void 0;
7
7
  exports.defaults = defaults;
8
8
  exports.escape = escape;
9
9
  exports.extend = extend;
10
+ exports.hasUnsafeKeySegment = hasUnsafeKeySegment;
10
11
  exports.hasXSS = hasXSS;
11
12
  exports.isSafeIdentifier = void 0;
12
13
  exports.isSafeLangIdentifier = isSafeLangIdentifier;
@@ -14,6 +15,9 @@ exports.isSafeNsIdentifier = isSafeNsIdentifier;
14
15
  exports.removeLngFromUrl = removeLngFromUrl;
15
16
  exports.sanitizeHeaderValue = sanitizeHeaderValue;
16
17
  exports.setPath = setPath;
18
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
19
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
20
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
17
21
  var UNSAFE_KEYS = exports.UNSAFE_KEYS = ['__proto__', 'constructor', 'prototype'];
18
22
  function isSafeIdentifierBase(v) {
19
23
  if (typeof v !== 'string') return false;
@@ -33,6 +37,27 @@ function isSafeNsIdentifier(v) {
33
37
  return isSafeIdentifierBase(v);
34
38
  }
35
39
  var isSafeIdentifier = exports.isSafeIdentifier = isSafeLangIdentifier;
40
+ function hasUnsafeKeySegment(key, keySeparator) {
41
+ if (typeof key !== 'string') return false;
42
+ if (UNSAFE_KEYS.indexOf(key) > -1) return true;
43
+ if (keySeparator === false) return false;
44
+ var sep = keySeparator || '.';
45
+ if (key.indexOf(sep) < 0) return false;
46
+ var segments = key.split(sep);
47
+ var _iterator = _createForOfIteratorHelper(segments),
48
+ _step;
49
+ try {
50
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
51
+ var s = _step.value;
52
+ if (UNSAFE_KEYS.indexOf(s) > -1) return true;
53
+ }
54
+ } catch (err) {
55
+ _iterator.e(err);
56
+ } finally {
57
+ _iterator.f();
58
+ }
59
+ return false;
60
+ }
36
61
  function setPath(object, path, newValue) {
37
62
  var stack;
38
63
  if (typeof path !== 'string') stack = [].concat(path);
package/esm/index.js CHANGED
@@ -268,11 +268,12 @@ export function missingKeyHandler(i18next) {
268
268
  return options.send(res, 'i18next-express-middleware:: no backend configured');
269
269
  }
270
270
  var body = options.getBody(req);
271
+ var keySeparator = i18next.options && i18next.options.keySeparator;
271
272
  var saveMissingKeys = function saveMissingKeys(src) {
272
273
  if (!src || _typeof(src) !== 'object') return;
273
274
  for (var _i = 0, _Object$keys = Object.keys(src); _i < _Object$keys.length; _i++) {
274
275
  var m = _Object$keys[_i];
275
- if (utils.UNSAFE_KEYS.indexOf(m) > -1) continue;
276
+ if (utils.hasUnsafeKeySegment(m, keySeparator)) continue;
276
277
  i18next.services.backendConnector.saveMissing([lng], ns, m, src[m]);
277
278
  }
278
279
  };
package/esm/utils.js CHANGED
@@ -1,3 +1,6 @@
1
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
2
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
3
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
1
4
  export var UNSAFE_KEYS = ['__proto__', 'constructor', 'prototype'];
2
5
  function isSafeIdentifierBase(v) {
3
6
  if (typeof v !== 'string') return false;
@@ -17,6 +20,27 @@ export function isSafeNsIdentifier(v) {
17
20
  return isSafeIdentifierBase(v);
18
21
  }
19
22
  export var isSafeIdentifier = isSafeLangIdentifier;
23
+ export function hasUnsafeKeySegment(key, keySeparator) {
24
+ if (typeof key !== 'string') return false;
25
+ if (UNSAFE_KEYS.indexOf(key) > -1) return true;
26
+ if (keySeparator === false) return false;
27
+ var sep = keySeparator || '.';
28
+ if (key.indexOf(sep) < 0) return false;
29
+ var segments = key.split(sep);
30
+ var _iterator = _createForOfIteratorHelper(segments),
31
+ _step;
32
+ try {
33
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
34
+ var s = _step.value;
35
+ if (UNSAFE_KEYS.indexOf(s) > -1) return true;
36
+ }
37
+ } catch (err) {
38
+ _iterator.e(err);
39
+ } finally {
40
+ _iterator.f();
41
+ }
42
+ return false;
43
+ }
20
44
  export function setPath(object, path, newValue) {
21
45
  var stack;
22
46
  if (typeof path !== 'string') stack = [].concat(path);
package/lib/index.js CHANGED
@@ -311,12 +311,15 @@ export function missingKeyHandler (i18next, options = {}) {
311
311
  }
312
312
 
313
313
  const body = options.getBody(req)
314
+ const keySeparator = i18next.options && i18next.options.keySeparator
314
315
 
315
- // iterate only over own, non-prototype-polluting keys
316
+ // iterate only over own, non-prototype-polluting keys. The check also
317
+ // rejects dotted variants like `__proto__.polluted` whose segments under
318
+ // the configured keySeparator land on an unsafe key — see utils.js.
316
319
  const saveMissingKeys = src => {
317
320
  if (!src || typeof src !== 'object') return
318
321
  for (const m of Object.keys(src)) {
319
- if (utils.UNSAFE_KEYS.indexOf(m) > -1) continue
322
+ if (utils.hasUnsafeKeySegment(m, keySeparator)) continue
320
323
  i18next.services.backendConnector.saveMissing([lng], ns, m, src[m])
321
324
  }
322
325
  }
package/lib/utils.js CHANGED
@@ -43,6 +43,27 @@ export function isSafeNsIdentifier (v) {
43
43
  // exported this symbol; external callers (if any) get the pre-fix behaviour.
44
44
  export const isSafeIdentifier = isSafeLangIdentifier
45
45
 
46
+ // Defense-in-depth check for `missingKeyHandler`. The literal-key denylist
47
+ // catches `{"__proto__": ...}` but misses dotted variants like
48
+ // `{"__proto__.polluted": "x"}` — backends that split the missing-key string
49
+ // on a configured keySeparator (e.g. i18next-fs-backend's writeFile()) would
50
+ // hand that key to an unguarded setPath and walk into Object.prototype.
51
+ // Reject any key whose segments — under the configured keySeparator (default
52
+ // `.`) — include an unsafe key. keySeparator === false means "no splitting",
53
+ // in which case only the literal-key check applies.
54
+ export function hasUnsafeKeySegment (key, keySeparator) {
55
+ if (typeof key !== 'string') return false
56
+ if (UNSAFE_KEYS.indexOf(key) > -1) return true
57
+ if (keySeparator === false) return false
58
+ const sep = keySeparator || '.'
59
+ if (key.indexOf(sep) < 0) return false
60
+ const segments = key.split(sep)
61
+ for (const s of segments) {
62
+ if (UNSAFE_KEYS.indexOf(s) > -1) return true
63
+ }
64
+ return false
65
+ }
66
+
46
67
  export function setPath (object, path, newValue) {
47
68
  let stack
48
69
  if (typeof path !== 'string') stack = [].concat(path)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-http-middleware",
3
- "version": "3.9.6",
3
+ "version": "3.9.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./cjs/index.js",