i18next-http-middleware 3.9.2 → 3.9.4
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 +0 -1
- package/cjs/LanguageDetector.js +1 -2
- package/cjs/index.js +42 -33
- package/cjs/languageLookups/cookie.js +2 -0
- package/cjs/utils.js +20 -1
- package/esm/index.js +40 -30
- package/esm/languageLookups/cookie.js +2 -0
- package/esm/utils.js +17 -1
- package/lib/index.js +32 -9
- package/lib/languageLookups/cookie.js +7 -0
- package/lib/utils.js +35 -2
- package/licence +1 -1
- package/package.json +18 -18
package/README.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/i18next/i18next-http-middleware/actions?query=workflow%3Anode)
|
|
4
4
|
[](https://github.com/i18next/i18next-http-middleware/actions?query=workflow%3Adeno)
|
|
5
|
-
[](https://travis-ci.org/i18next/i18next-http-middleware)
|
|
6
5
|
[](https://www.npmjs.com/package/i18next-http-middleware)
|
|
7
6
|
|
|
8
7
|
This is a middleware to be used with Node.js web frameworks like express or Fastify and also for Deno.
|
package/cjs/LanguageDetector.js
CHANGED
|
@@ -12,8 +12,7 @@ var _header = _interopRequireDefault(require("./languageLookups/header.js"));
|
|
|
12
12
|
var _session = _interopRequireDefault(require("./languageLookups/session.js"));
|
|
13
13
|
var _httpFunctions = require("./httpFunctions.js");
|
|
14
14
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
-
function
|
|
16
|
-
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
15
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
17
16
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
18
17
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
19
18
|
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
package/cjs/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
3
|
Object.defineProperty(exports, "__esModule", {
|
|
5
4
|
value: true
|
|
6
5
|
});
|
|
@@ -18,14 +17,15 @@ var utils = _interopRequireWildcard(require("./utils.js"));
|
|
|
18
17
|
var _LanguageDetector = _interopRequireDefault(require("./LanguageDetector.js"));
|
|
19
18
|
var _httpFunctions = require("./httpFunctions.js");
|
|
20
19
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
|
-
function
|
|
22
|
-
function
|
|
20
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
21
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
23
22
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
24
23
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
25
24
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
26
25
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
27
26
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
28
|
-
function
|
|
27
|
+
function _regenerator() { var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
28
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
29
29
|
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
30
30
|
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
31
31
|
var LanguageDetector = exports.LanguageDetector = _LanguageDetector.default;
|
|
@@ -93,7 +93,7 @@ function handle(i18next) {
|
|
|
93
93
|
res.locals.resolvedLanguage = i18n.resolvedLanguage;
|
|
94
94
|
}
|
|
95
95
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
96
|
-
options.setHeader(res, 'Content-Language', utils.
|
|
96
|
+
options.setHeader(res, 'Content-Language', utils.sanitizeHeaderValue(lng));
|
|
97
97
|
}
|
|
98
98
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
|
|
99
99
|
req.resolvedLanguage = i18n.resolvedLanguage;
|
|
@@ -107,7 +107,7 @@ function handle(i18next) {
|
|
|
107
107
|
}
|
|
108
108
|
req.language = req.locale = req.lng = lng;
|
|
109
109
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
110
|
-
options.setHeader(res, 'Content-Language', utils.
|
|
110
|
+
options.setHeader(res, 'Content-Language', utils.sanitizeHeaderValue(lng));
|
|
111
111
|
}
|
|
112
112
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
|
|
113
113
|
req.resolvedLanguage = i18n.resolvedLanguage;
|
|
@@ -148,20 +148,19 @@ function plugin(instance, options, next) {
|
|
|
148
148
|
function koaPlugin(i18next, options) {
|
|
149
149
|
var middleware = handle(i18next, options);
|
|
150
150
|
return function () {
|
|
151
|
-
var _koaMiddleware = _asyncToGenerator(
|
|
152
|
-
return
|
|
153
|
-
while (1) switch (_context.
|
|
151
|
+
var _koaMiddleware = _asyncToGenerator(_regenerator().m(function _callee(ctx, next) {
|
|
152
|
+
return _regenerator().w(function (_context) {
|
|
153
|
+
while (1) switch (_context.n) {
|
|
154
154
|
case 0:
|
|
155
|
-
_context.
|
|
155
|
+
_context.n = 1;
|
|
156
156
|
return new Promise(function (resolve) {
|
|
157
157
|
return middleware(ctx, ctx, resolve);
|
|
158
158
|
});
|
|
159
|
-
case
|
|
160
|
-
_context.
|
|
159
|
+
case 1:
|
|
160
|
+
_context.n = 2;
|
|
161
161
|
return next();
|
|
162
|
-
case
|
|
163
|
-
|
|
164
|
-
return _context.stop();
|
|
162
|
+
case 2:
|
|
163
|
+
return _context.a(2);
|
|
165
164
|
}
|
|
166
165
|
}, _callee);
|
|
167
166
|
}));
|
|
@@ -173,10 +172,10 @@ function koaPlugin(i18next, options) {
|
|
|
173
172
|
}
|
|
174
173
|
function freshPlugin(i18next, options) {
|
|
175
174
|
return function () {
|
|
176
|
-
var _ref = _asyncToGenerator(
|
|
177
|
-
var placeholder, resp;
|
|
178
|
-
return
|
|
179
|
-
while (1) switch (_context2.
|
|
175
|
+
var _ref = _asyncToGenerator(_regenerator().m(function _callee2(ctx) {
|
|
176
|
+
var placeholder, resp, contentLanguage, setCookie;
|
|
177
|
+
return _regenerator().w(function (_context2) {
|
|
178
|
+
while (1) switch (_context2.n) {
|
|
180
179
|
case 0:
|
|
181
180
|
handle(i18next, _objectSpread(_objectSpread({}, options), {}, {
|
|
182
181
|
attachLocals: true
|
|
@@ -189,15 +188,19 @@ function freshPlugin(i18next, options) {
|
|
|
189
188
|
ctx.state.languages = ctx.req.languages;
|
|
190
189
|
placeholder = ctx.state.res;
|
|
191
190
|
delete ctx.state.res;
|
|
192
|
-
_context2.
|
|
191
|
+
_context2.n = 1;
|
|
193
192
|
return ctx.next();
|
|
194
|
-
case
|
|
195
|
-
resp = _context2.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
193
|
+
case 1:
|
|
194
|
+
resp = _context2.v;
|
|
195
|
+
contentLanguage = placeholder.headers.get('Content-Language');
|
|
196
|
+
if (contentLanguage) {
|
|
197
|
+
resp.headers.set('Content-Language', contentLanguage);
|
|
198
|
+
}
|
|
199
|
+
setCookie = placeholder.headers.get('Set-Cookie');
|
|
200
|
+
if (setCookie) {
|
|
201
|
+
resp.headers.set('Set-Cookie', setCookie);
|
|
202
|
+
}
|
|
203
|
+
return _context2.a(2, resp);
|
|
201
204
|
}
|
|
202
205
|
}, _callee2);
|
|
203
206
|
}));
|
|
@@ -248,6 +251,8 @@ function getResourcesHandler(i18next) {
|
|
|
248
251
|
languages = options.getParams(req)[options.lngParam || 'lng'] ? options.getParams(req)[options.lngParam || 'lng'].split(' ') : [];
|
|
249
252
|
namespaces = options.getParams(req)[options.nsParam || 'ns'] ? options.getParams(req)[options.nsParam || 'ns'].split(' ') : [];
|
|
250
253
|
}
|
|
254
|
+
languages = languages.filter(utils.isSafeIdentifier);
|
|
255
|
+
namespaces = namespaces.filter(utils.isSafeIdentifier);
|
|
251
256
|
namespaces.forEach(function (ns) {
|
|
252
257
|
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) {
|
|
253
258
|
i18next.options.ns.push(ns);
|
|
@@ -280,22 +285,26 @@ function missingKeyHandler(i18next) {
|
|
|
280
285
|
return options.send(res, 'i18next-express-middleware:: no backend configured');
|
|
281
286
|
}
|
|
282
287
|
var body = options.getBody(req);
|
|
288
|
+
var saveMissingKeys = function saveMissingKeys(src) {
|
|
289
|
+
if (!src || _typeof(src) !== 'object') return;
|
|
290
|
+
for (var _i = 0, _Object$keys = Object.keys(src); _i < _Object$keys.length; _i++) {
|
|
291
|
+
var m = _Object$keys[_i];
|
|
292
|
+
if (utils.UNSAFE_KEYS.indexOf(m) > -1) continue;
|
|
293
|
+
i18next.services.backendConnector.saveMissing([lng], ns, m, src[m]);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
283
296
|
if (typeof body === 'function') {
|
|
284
297
|
var promise = body();
|
|
285
298
|
if (promise && typeof promise.then === 'function') {
|
|
286
299
|
return new Promise(function (resolve) {
|
|
287
300
|
promise.then(function (b) {
|
|
288
|
-
|
|
289
|
-
i18next.services.backendConnector.saveMissing([lng], ns, m, b[m]);
|
|
290
|
-
}
|
|
301
|
+
saveMissingKeys(b);
|
|
291
302
|
resolve(options.send(res, 'ok'));
|
|
292
303
|
});
|
|
293
304
|
});
|
|
294
305
|
}
|
|
295
306
|
}
|
|
296
|
-
|
|
297
|
-
i18next.services.backendConnector.saveMissing([lng], ns, m, body[m]);
|
|
298
|
-
}
|
|
307
|
+
saveMissingKeys(body);
|
|
299
308
|
return options.send(res, 'ok');
|
|
300
309
|
};
|
|
301
310
|
}
|
|
@@ -84,6 +84,8 @@ var _default = exports.default = {
|
|
|
84
84
|
sameSite: options.cookieSameSite
|
|
85
85
|
};
|
|
86
86
|
if (options.cookieSecure) cookieOptions.secure = options.cookieSecure;
|
|
87
|
+
var sameSiteNormalised = typeof cookieOptions.sameSite === 'string' ? cookieOptions.sameSite.toLowerCase() : cookieOptions.sameSite;
|
|
88
|
+
if (sameSiteNormalised === 'none') cookieOptions.secure = true;
|
|
87
89
|
var existingCookie = options.getHeader(res, 'set-cookie') || options.getHeader(res, 'Set-Cookie') || [];
|
|
88
90
|
if (typeof existingCookie === 'string') existingCookie = [existingCookie];
|
|
89
91
|
if (!Array.isArray(existingCookie)) existingCookie = [];
|
package/cjs/utils.js
CHANGED
|
@@ -3,12 +3,25 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.UNSAFE_KEYS = void 0;
|
|
6
7
|
exports.defaults = defaults;
|
|
7
8
|
exports.escape = escape;
|
|
8
9
|
exports.extend = extend;
|
|
9
10
|
exports.hasXSS = hasXSS;
|
|
11
|
+
exports.isSafeIdentifier = isSafeIdentifier;
|
|
10
12
|
exports.removeLngFromUrl = removeLngFromUrl;
|
|
13
|
+
exports.sanitizeHeaderValue = sanitizeHeaderValue;
|
|
11
14
|
exports.setPath = setPath;
|
|
15
|
+
var UNSAFE_KEYS = exports.UNSAFE_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
16
|
+
function isSafeIdentifier(v) {
|
|
17
|
+
if (typeof v !== 'string') return false;
|
|
18
|
+
if (v.length === 0 || v.length > 128) return false;
|
|
19
|
+
if (UNSAFE_KEYS.indexOf(v) > -1) return false;
|
|
20
|
+
if (v.indexOf('..') > -1) return false;
|
|
21
|
+
if (v.indexOf('/') > -1 || v.indexOf('\\') > -1) return false;
|
|
22
|
+
if (/[\x00-\x1F\x7F]/.test(v)) return false;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
12
25
|
function setPath(object, path, newValue) {
|
|
13
26
|
var stack;
|
|
14
27
|
if (typeof path !== 'string') stack = [].concat(path);
|
|
@@ -16,11 +29,13 @@ function setPath(object, path, newValue) {
|
|
|
16
29
|
while (stack.length > 1) {
|
|
17
30
|
var _key = stack.shift();
|
|
18
31
|
if (_key.indexOf('###') > -1) _key = _key.replace(/###/g, '.');
|
|
32
|
+
if (UNSAFE_KEYS.indexOf(_key) > -1) return;
|
|
19
33
|
if (!object[_key]) object[_key] = {};
|
|
20
34
|
object = object[_key];
|
|
21
35
|
}
|
|
22
36
|
var key = stack.shift();
|
|
23
37
|
if (key.indexOf('###') > -1) key = key.replace(/###/g, '.');
|
|
38
|
+
if (UNSAFE_KEYS.indexOf(key) > -1) return;
|
|
24
39
|
object[key] = newValue;
|
|
25
40
|
}
|
|
26
41
|
var arr = [];
|
|
@@ -62,9 +77,13 @@ function removeLngFromUrl(url, lookupFromPathIndex) {
|
|
|
62
77
|
function escape(str) {
|
|
63
78
|
return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>').replace(/\//g, '/').replace(/\\/g, '\').replace(/`/g, '`');
|
|
64
79
|
}
|
|
80
|
+
function sanitizeHeaderValue(str) {
|
|
81
|
+
if (typeof str !== 'string') return str;
|
|
82
|
+
return str.replace(/[\r\n\x00-\x1F\x7F]/g, '');
|
|
83
|
+
}
|
|
65
84
|
function hasXSS(input) {
|
|
66
85
|
if (typeof input !== 'string') return false;
|
|
67
|
-
var xssPatterns = [/<\s*script.*?>/i, /<\s*\/\s*script\s*>/i, /<\s
|
|
86
|
+
var xssPatterns = [/<\s*script.*?>/i, /<\s*\/\s*script\s*>/i, /<\s*\w+\s+[^>]*?\bon\w+\s*=/i, /javascript\s*:/i, /vbscript\s*:/i, /expression\s*\(/i, /eval\s*\(/i, /alert\s*\(/i, /document\.cookie/i, /document\.write\s*\(/i, /window\.location/i, /innerHTML/i];
|
|
68
87
|
return xssPatterns.some(function (pattern) {
|
|
69
88
|
return pattern.test(input);
|
|
70
89
|
});
|
package/esm/index.js
CHANGED
|
@@ -4,7 +4,8 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
4
4
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
5
5
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
6
6
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7
|
-
function
|
|
7
|
+
function _regenerator() { var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
8
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
8
9
|
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
9
10
|
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
10
11
|
import * as utils from './utils.js';
|
|
@@ -75,7 +76,7 @@ export function handle(i18next) {
|
|
|
75
76
|
res.locals.resolvedLanguage = i18n.resolvedLanguage;
|
|
76
77
|
}
|
|
77
78
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
78
|
-
options.setHeader(res, 'Content-Language', utils.
|
|
79
|
+
options.setHeader(res, 'Content-Language', utils.sanitizeHeaderValue(lng));
|
|
79
80
|
}
|
|
80
81
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
|
|
81
82
|
req.resolvedLanguage = i18n.resolvedLanguage;
|
|
@@ -89,7 +90,7 @@ export function handle(i18next) {
|
|
|
89
90
|
}
|
|
90
91
|
req.language = req.locale = req.lng = lng;
|
|
91
92
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
92
|
-
options.setHeader(res, 'Content-Language', utils.
|
|
93
|
+
options.setHeader(res, 'Content-Language', utils.sanitizeHeaderValue(lng));
|
|
93
94
|
}
|
|
94
95
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
|
|
95
96
|
req.resolvedLanguage = i18n.resolvedLanguage;
|
|
@@ -130,20 +131,19 @@ export function plugin(instance, options, next) {
|
|
|
130
131
|
export function koaPlugin(i18next, options) {
|
|
131
132
|
var middleware = handle(i18next, options);
|
|
132
133
|
return function () {
|
|
133
|
-
var _koaMiddleware = _asyncToGenerator(
|
|
134
|
-
return
|
|
135
|
-
while (1) switch (_context.
|
|
134
|
+
var _koaMiddleware = _asyncToGenerator(_regenerator().m(function _callee(ctx, next) {
|
|
135
|
+
return _regenerator().w(function (_context) {
|
|
136
|
+
while (1) switch (_context.n) {
|
|
136
137
|
case 0:
|
|
137
|
-
_context.
|
|
138
|
+
_context.n = 1;
|
|
138
139
|
return new Promise(function (resolve) {
|
|
139
140
|
return middleware(ctx, ctx, resolve);
|
|
140
141
|
});
|
|
141
|
-
case
|
|
142
|
-
_context.
|
|
142
|
+
case 1:
|
|
143
|
+
_context.n = 2;
|
|
143
144
|
return next();
|
|
144
|
-
case
|
|
145
|
-
|
|
146
|
-
return _context.stop();
|
|
145
|
+
case 2:
|
|
146
|
+
return _context.a(2);
|
|
147
147
|
}
|
|
148
148
|
}, _callee);
|
|
149
149
|
}));
|
|
@@ -155,10 +155,10 @@ export function koaPlugin(i18next, options) {
|
|
|
155
155
|
}
|
|
156
156
|
export function freshPlugin(i18next, options) {
|
|
157
157
|
return function () {
|
|
158
|
-
var _ref = _asyncToGenerator(
|
|
159
|
-
var placeholder, resp;
|
|
160
|
-
return
|
|
161
|
-
while (1) switch (_context2.
|
|
158
|
+
var _ref = _asyncToGenerator(_regenerator().m(function _callee2(ctx) {
|
|
159
|
+
var placeholder, resp, contentLanguage, setCookie;
|
|
160
|
+
return _regenerator().w(function (_context2) {
|
|
161
|
+
while (1) switch (_context2.n) {
|
|
162
162
|
case 0:
|
|
163
163
|
handle(i18next, _objectSpread(_objectSpread({}, options), {}, {
|
|
164
164
|
attachLocals: true
|
|
@@ -171,15 +171,19 @@ export function freshPlugin(i18next, options) {
|
|
|
171
171
|
ctx.state.languages = ctx.req.languages;
|
|
172
172
|
placeholder = ctx.state.res;
|
|
173
173
|
delete ctx.state.res;
|
|
174
|
-
_context2.
|
|
174
|
+
_context2.n = 1;
|
|
175
175
|
return ctx.next();
|
|
176
|
-
case
|
|
177
|
-
resp = _context2.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
176
|
+
case 1:
|
|
177
|
+
resp = _context2.v;
|
|
178
|
+
contentLanguage = placeholder.headers.get('Content-Language');
|
|
179
|
+
if (contentLanguage) {
|
|
180
|
+
resp.headers.set('Content-Language', contentLanguage);
|
|
181
|
+
}
|
|
182
|
+
setCookie = placeholder.headers.get('Set-Cookie');
|
|
183
|
+
if (setCookie) {
|
|
184
|
+
resp.headers.set('Set-Cookie', setCookie);
|
|
185
|
+
}
|
|
186
|
+
return _context2.a(2, resp);
|
|
183
187
|
}
|
|
184
188
|
}, _callee2);
|
|
185
189
|
}));
|
|
@@ -230,6 +234,8 @@ export function getResourcesHandler(i18next) {
|
|
|
230
234
|
languages = options.getParams(req)[options.lngParam || 'lng'] ? options.getParams(req)[options.lngParam || 'lng'].split(' ') : [];
|
|
231
235
|
namespaces = options.getParams(req)[options.nsParam || 'ns'] ? options.getParams(req)[options.nsParam || 'ns'].split(' ') : [];
|
|
232
236
|
}
|
|
237
|
+
languages = languages.filter(utils.isSafeIdentifier);
|
|
238
|
+
namespaces = namespaces.filter(utils.isSafeIdentifier);
|
|
233
239
|
namespaces.forEach(function (ns) {
|
|
234
240
|
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) {
|
|
235
241
|
i18next.options.ns.push(ns);
|
|
@@ -262,22 +268,26 @@ export function missingKeyHandler(i18next) {
|
|
|
262
268
|
return options.send(res, 'i18next-express-middleware:: no backend configured');
|
|
263
269
|
}
|
|
264
270
|
var body = options.getBody(req);
|
|
271
|
+
var saveMissingKeys = function saveMissingKeys(src) {
|
|
272
|
+
if (!src || _typeof(src) !== 'object') return;
|
|
273
|
+
for (var _i = 0, _Object$keys = Object.keys(src); _i < _Object$keys.length; _i++) {
|
|
274
|
+
var m = _Object$keys[_i];
|
|
275
|
+
if (utils.UNSAFE_KEYS.indexOf(m) > -1) continue;
|
|
276
|
+
i18next.services.backendConnector.saveMissing([lng], ns, m, src[m]);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
265
279
|
if (typeof body === 'function') {
|
|
266
280
|
var promise = body();
|
|
267
281
|
if (promise && typeof promise.then === 'function') {
|
|
268
282
|
return new Promise(function (resolve) {
|
|
269
283
|
promise.then(function (b) {
|
|
270
|
-
|
|
271
|
-
i18next.services.backendConnector.saveMissing([lng], ns, m, b[m]);
|
|
272
|
-
}
|
|
284
|
+
saveMissingKeys(b);
|
|
273
285
|
resolve(options.send(res, 'ok'));
|
|
274
286
|
});
|
|
275
287
|
});
|
|
276
288
|
}
|
|
277
289
|
}
|
|
278
|
-
|
|
279
|
-
i18next.services.backendConnector.saveMissing([lng], ns, m, body[m]);
|
|
280
|
-
}
|
|
290
|
+
saveMissingKeys(body);
|
|
281
291
|
return options.send(res, 'ok');
|
|
282
292
|
};
|
|
283
293
|
}
|
|
@@ -78,6 +78,8 @@ export default {
|
|
|
78
78
|
sameSite: options.cookieSameSite
|
|
79
79
|
};
|
|
80
80
|
if (options.cookieSecure) cookieOptions.secure = options.cookieSecure;
|
|
81
|
+
var sameSiteNormalised = typeof cookieOptions.sameSite === 'string' ? cookieOptions.sameSite.toLowerCase() : cookieOptions.sameSite;
|
|
82
|
+
if (sameSiteNormalised === 'none') cookieOptions.secure = true;
|
|
81
83
|
var existingCookie = options.getHeader(res, 'set-cookie') || options.getHeader(res, 'Set-Cookie') || [];
|
|
82
84
|
if (typeof existingCookie === 'string') existingCookie = [existingCookie];
|
|
83
85
|
if (!Array.isArray(existingCookie)) existingCookie = [];
|
package/esm/utils.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
export var UNSAFE_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
2
|
+
export function isSafeIdentifier(v) {
|
|
3
|
+
if (typeof v !== 'string') return false;
|
|
4
|
+
if (v.length === 0 || v.length > 128) return false;
|
|
5
|
+
if (UNSAFE_KEYS.indexOf(v) > -1) return false;
|
|
6
|
+
if (v.indexOf('..') > -1) return false;
|
|
7
|
+
if (v.indexOf('/') > -1 || v.indexOf('\\') > -1) return false;
|
|
8
|
+
if (/[\x00-\x1F\x7F]/.test(v)) return false;
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
1
11
|
export function setPath(object, path, newValue) {
|
|
2
12
|
var stack;
|
|
3
13
|
if (typeof path !== 'string') stack = [].concat(path);
|
|
@@ -5,11 +15,13 @@ export function setPath(object, path, newValue) {
|
|
|
5
15
|
while (stack.length > 1) {
|
|
6
16
|
var _key = stack.shift();
|
|
7
17
|
if (_key.indexOf('###') > -1) _key = _key.replace(/###/g, '.');
|
|
18
|
+
if (UNSAFE_KEYS.indexOf(_key) > -1) return;
|
|
8
19
|
if (!object[_key]) object[_key] = {};
|
|
9
20
|
object = object[_key];
|
|
10
21
|
}
|
|
11
22
|
var key = stack.shift();
|
|
12
23
|
if (key.indexOf('###') > -1) key = key.replace(/###/g, '.');
|
|
24
|
+
if (UNSAFE_KEYS.indexOf(key) > -1) return;
|
|
13
25
|
object[key] = newValue;
|
|
14
26
|
}
|
|
15
27
|
var arr = [];
|
|
@@ -51,9 +63,13 @@ export function removeLngFromUrl(url, lookupFromPathIndex) {
|
|
|
51
63
|
export function escape(str) {
|
|
52
64
|
return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>').replace(/\//g, '/').replace(/\\/g, '\').replace(/`/g, '`');
|
|
53
65
|
}
|
|
66
|
+
export function sanitizeHeaderValue(str) {
|
|
67
|
+
if (typeof str !== 'string') return str;
|
|
68
|
+
return str.replace(/[\r\n\x00-\x1F\x7F]/g, '');
|
|
69
|
+
}
|
|
54
70
|
export function hasXSS(input) {
|
|
55
71
|
if (typeof input !== 'string') return false;
|
|
56
|
-
var xssPatterns = [/<\s*script.*?>/i, /<\s*\/\s*script\s*>/i, /<\s
|
|
72
|
+
var xssPatterns = [/<\s*script.*?>/i, /<\s*\/\s*script\s*>/i, /<\s*\w+\s+[^>]*?\bon\w+\s*=/i, /javascript\s*:/i, /vbscript\s*:/i, /expression\s*\(/i, /eval\s*\(/i, /alert\s*\(/i, /document\.cookie/i, /document\.write\s*\(/i, /window\.location/i, /innerHTML/i];
|
|
57
73
|
return xssPatterns.some(function (pattern) {
|
|
58
74
|
return pattern.test(input);
|
|
59
75
|
});
|
package/lib/index.js
CHANGED
|
@@ -69,7 +69,7 @@ export function handle (i18next, options = {}) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
72
|
-
options.setHeader(res, 'Content-Language', utils.
|
|
72
|
+
options.setHeader(res, 'Content-Language', utils.sanitizeHeaderValue(lng))
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng)
|
|
@@ -88,7 +88,7 @@ export function handle (i18next, options = {}) {
|
|
|
88
88
|
// set locale
|
|
89
89
|
req.language = req.locale = req.lng = lng
|
|
90
90
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
91
|
-
options.setHeader(res, 'Content-Language', utils.
|
|
91
|
+
options.setHeader(res, 'Content-Language', utils.sanitizeHeaderValue(lng))
|
|
92
92
|
}
|
|
93
93
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng)
|
|
94
94
|
req.resolvedLanguage = i18n.resolvedLanguage
|
|
@@ -162,7 +162,17 @@ export function freshPlugin (i18next, options) {
|
|
|
162
162
|
const placeholder = ctx.state.res
|
|
163
163
|
delete ctx.state.res
|
|
164
164
|
const resp = await ctx.next()
|
|
165
|
-
|
|
165
|
+
|
|
166
|
+
const contentLanguage = placeholder.headers.get('Content-Language')
|
|
167
|
+
if (contentLanguage) {
|
|
168
|
+
resp.headers.set('Content-Language', contentLanguage)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const setCookie = placeholder.headers.get('Set-Cookie')
|
|
172
|
+
if (setCookie) {
|
|
173
|
+
resp.headers.set('Set-Cookie', setCookie)
|
|
174
|
+
}
|
|
175
|
+
|
|
166
176
|
return resp
|
|
167
177
|
}
|
|
168
178
|
}
|
|
@@ -243,6 +253,14 @@ export function getResourcesHandler (i18next, options = {}) {
|
|
|
243
253
|
: []
|
|
244
254
|
}
|
|
245
255
|
|
|
256
|
+
// Drop user-supplied values containing patterns that could trigger path
|
|
257
|
+
// traversal / SSRF / prototype pollution when forwarded to the backend
|
|
258
|
+
// connector. i18next itself permits arbitrary language codes, so we do
|
|
259
|
+
// not impose a BCP-47 shape — we only block known-dangerous patterns.
|
|
260
|
+
// See: https://www.i18next.com/how-to/faq#how-should-the-language-codes-be-formatted
|
|
261
|
+
languages = languages.filter(utils.isSafeIdentifier)
|
|
262
|
+
namespaces = namespaces.filter(utils.isSafeIdentifier)
|
|
263
|
+
|
|
246
264
|
// extend ns
|
|
247
265
|
namespaces.forEach(ns => {
|
|
248
266
|
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) {
|
|
@@ -290,23 +308,28 @@ export function missingKeyHandler (i18next, options = {}) {
|
|
|
290
308
|
|
|
291
309
|
const body = options.getBody(req)
|
|
292
310
|
|
|
311
|
+
// iterate only over own, non-prototype-polluting keys
|
|
312
|
+
const saveMissingKeys = src => {
|
|
313
|
+
if (!src || typeof src !== 'object') return
|
|
314
|
+
for (const m of Object.keys(src)) {
|
|
315
|
+
if (utils.UNSAFE_KEYS.indexOf(m) > -1) continue
|
|
316
|
+
i18next.services.backendConnector.saveMissing([lng], ns, m, src[m])
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
293
320
|
if (typeof body === 'function') {
|
|
294
321
|
const promise = body()
|
|
295
322
|
if (promise && typeof promise.then === 'function') {
|
|
296
323
|
return new Promise(resolve => {
|
|
297
324
|
promise.then(b => {
|
|
298
|
-
|
|
299
|
-
i18next.services.backendConnector.saveMissing([lng], ns, m, b[m])
|
|
300
|
-
}
|
|
325
|
+
saveMissingKeys(b)
|
|
301
326
|
resolve(options.send(res, 'ok'))
|
|
302
327
|
})
|
|
303
328
|
})
|
|
304
329
|
}
|
|
305
330
|
}
|
|
306
331
|
|
|
307
|
-
|
|
308
|
-
i18next.services.backendConnector.saveMissing([lng], ns, m, body[m])
|
|
309
|
-
}
|
|
332
|
+
saveMissingKeys(body)
|
|
310
333
|
|
|
311
334
|
return options.send(res, 'ok')
|
|
312
335
|
}
|
|
@@ -86,6 +86,13 @@ export default {
|
|
|
86
86
|
|
|
87
87
|
if (options.cookieSecure) cookieOptions.secure = options.cookieSecure
|
|
88
88
|
|
|
89
|
+
// SameSite=None requires Secure — browsers reject the cookie otherwise.
|
|
90
|
+
// Force Secure in that case so misconfigured setups still function.
|
|
91
|
+
const sameSiteNormalised = typeof cookieOptions.sameSite === 'string'
|
|
92
|
+
? cookieOptions.sameSite.toLowerCase()
|
|
93
|
+
: cookieOptions.sameSite
|
|
94
|
+
if (sameSiteNormalised === 'none') cookieOptions.secure = true
|
|
95
|
+
|
|
89
96
|
let existingCookie = options.getHeader(res, 'set-cookie') || options.getHeader(res, 'Set-Cookie') || []
|
|
90
97
|
if (typeof existingCookie === 'string') existingCookie = [existingCookie]
|
|
91
98
|
if (!Array.isArray(existingCookie)) existingCookie = []
|
package/lib/utils.js
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
export const UNSAFE_KEYS = ['__proto__', 'constructor', 'prototype']
|
|
2
|
+
|
|
3
|
+
// Returns true if `v` can be safely forwarded as a language code or namespace
|
|
4
|
+
// identifier to a backend connector. Denylist approach (i18next permits any
|
|
5
|
+
// language-code shape — https://www.i18next.com/how-to/faq#how-should-the-language-codes-be-formatted)
|
|
6
|
+
// that blocks the concrete attack patterns without restricting legitimate use:
|
|
7
|
+
// - not one of `__proto__` / `constructor` / `prototype` (prototype pollution)
|
|
8
|
+
// - no path separators `/` or `\` (path traversal / SSRF in fs/http backends)
|
|
9
|
+
// - no `..` sequence (relative path traversal)
|
|
10
|
+
// - no control characters (header injection downstream, log forging)
|
|
11
|
+
// - non-empty, length <= 128
|
|
12
|
+
export function isSafeIdentifier (v) {
|
|
13
|
+
if (typeof v !== 'string') return false
|
|
14
|
+
if (v.length === 0 || v.length > 128) return false
|
|
15
|
+
if (UNSAFE_KEYS.indexOf(v) > -1) return false
|
|
16
|
+
if (v.indexOf('..') > -1) return false
|
|
17
|
+
if (v.indexOf('/') > -1 || v.indexOf('\\') > -1) return false
|
|
18
|
+
// eslint-disable-next-line no-control-regex
|
|
19
|
+
if (/[\x00-\x1F\x7F]/.test(v)) return false
|
|
20
|
+
return true
|
|
21
|
+
}
|
|
22
|
+
|
|
1
23
|
export function setPath (object, path, newValue) {
|
|
2
24
|
let stack
|
|
3
25
|
if (typeof path !== 'string') stack = [].concat(path)
|
|
@@ -6,12 +28,14 @@ export function setPath (object, path, newValue) {
|
|
|
6
28
|
while (stack.length > 1) {
|
|
7
29
|
let key = stack.shift()
|
|
8
30
|
if (key.indexOf('###') > -1) key = key.replace(/###/g, '.')
|
|
31
|
+
if (UNSAFE_KEYS.indexOf(key) > -1) return // guard against prototype pollution
|
|
9
32
|
if (!object[key]) object[key] = {}
|
|
10
33
|
object = object[key]
|
|
11
34
|
}
|
|
12
35
|
|
|
13
36
|
let key = stack.shift()
|
|
14
37
|
if (key.indexOf('###') > -1) key = key.replace(/###/g, '.')
|
|
38
|
+
if (UNSAFE_KEYS.indexOf(key) > -1) return // guard against prototype pollution
|
|
15
39
|
object[key] = newValue
|
|
16
40
|
}
|
|
17
41
|
|
|
@@ -70,6 +94,15 @@ export function escape (str) {
|
|
|
70
94
|
.replace(/`/g, '`'))
|
|
71
95
|
}
|
|
72
96
|
|
|
97
|
+
// Strip control characters (CR, LF, NUL, other C0/C1) from a value before
|
|
98
|
+
// writing it into an HTTP header. Prevents HTTP response splitting on older
|
|
99
|
+
// Node.js, and unhandled ERR_INVALID_CHAR crashes on newer Node.js.
|
|
100
|
+
export function sanitizeHeaderValue (str) {
|
|
101
|
+
if (typeof str !== 'string') return str
|
|
102
|
+
// eslint-disable-next-line no-control-regex
|
|
103
|
+
return str.replace(/[\r\n\x00-\x1F\x7F]/g, '')
|
|
104
|
+
}
|
|
105
|
+
|
|
73
106
|
export function hasXSS (input) {
|
|
74
107
|
if (typeof input !== 'string') return false
|
|
75
108
|
|
|
@@ -77,8 +110,8 @@ export function hasXSS (input) {
|
|
|
77
110
|
const xssPatterns = [
|
|
78
111
|
/<\s*script.*?>/i,
|
|
79
112
|
/<\s*\/\s*script\s*>/i,
|
|
80
|
-
|
|
81
|
-
/<\s*\w+\s
|
|
113
|
+
// event handlers on any tag position (not just first attribute)
|
|
114
|
+
/<\s*\w+\s+[^>]*?\bon\w+\s*=/i,
|
|
82
115
|
/javascript\s*:/i,
|
|
83
116
|
/vbscript\s*:/i,
|
|
84
117
|
/expression\s*\(/i,
|
package/licence
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18next-http-middleware",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./cjs/index.js",
|
|
@@ -28,15 +28,15 @@
|
|
|
28
28
|
},
|
|
29
29
|
"module": "./esm/index.js",
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@babel/cli": "7.
|
|
32
|
-
"@babel/core": "7.
|
|
33
|
-
"@babel/preset-env": "7.
|
|
34
|
-
"@hapi/hapi": "^21.
|
|
35
|
-
"@opentelemetry/api": "^1.9.
|
|
36
|
-
"@preact/signals": "^2.
|
|
37
|
-
"@types/express-serve-static-core": "^5.
|
|
38
|
-
"@koa/router": "
|
|
39
|
-
"koa": "2.
|
|
31
|
+
"@babel/cli": "7.28.6",
|
|
32
|
+
"@babel/core": "7.29.0",
|
|
33
|
+
"@babel/preset-env": "7.29.2",
|
|
34
|
+
"@hapi/hapi": "^21.4.8",
|
|
35
|
+
"@opentelemetry/api": "^1.9.1",
|
|
36
|
+
"@preact/signals": "^2.9.0",
|
|
37
|
+
"@types/express-serve-static-core": "^5.1.1",
|
|
38
|
+
"@koa/router": "15.4.0",
|
|
39
|
+
"koa": "3.2.0",
|
|
40
40
|
"babel-plugin-add-module-exports": "1.0.4",
|
|
41
41
|
"eslint": "8.53.0",
|
|
42
42
|
"eslint-config-standard": "17.1.0",
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
"eslint-plugin-require-path-exists": "1.1.9",
|
|
47
47
|
"eslint-plugin-standard": "5.0.0",
|
|
48
48
|
"expect.js": "0.3.1",
|
|
49
|
-
"express": "
|
|
50
|
-
"fastify": "5.
|
|
51
|
-
"i18next": "
|
|
52
|
-
"mocha": "
|
|
53
|
-
"preact": "10.
|
|
54
|
-
"preact-render-to-string": "6.6.
|
|
55
|
-
"supertest": "7.
|
|
56
|
-
"tsd": "0.
|
|
49
|
+
"express": "5.2.1",
|
|
50
|
+
"fastify": "5.8.5",
|
|
51
|
+
"i18next": "26.0.5",
|
|
52
|
+
"mocha": "11.7.5",
|
|
53
|
+
"preact": "10.29.1",
|
|
54
|
+
"preact-render-to-string": "6.6.7",
|
|
55
|
+
"supertest": "7.2.2",
|
|
56
|
+
"tsd": "0.33.0",
|
|
57
57
|
"uglify-js": "3.19.3"
|
|
58
58
|
},
|
|
59
59
|
"description": "i18next-http-middleware is a middleware to be used with Node.js web frameworks like express or Fastify and also for Deno.",
|