i18next-http-middleware 3.9.4 → 3.9.6
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/cjs/index.js +3 -3
- package/cjs/utils.js +14 -3
- package/esm/index.js +3 -3
- package/esm/utils.js +11 -2
- package/lib/index.js +7 -3
- package/lib/utils.js +34 -11
- package/package.json +1 -1
package/cjs/index.js
CHANGED
|
@@ -198,7 +198,7 @@ function freshPlugin(i18next, options) {
|
|
|
198
198
|
}
|
|
199
199
|
setCookie = placeholder.headers.get('Set-Cookie');
|
|
200
200
|
if (setCookie) {
|
|
201
|
-
resp.headers.
|
|
201
|
+
resp.headers.append('Set-Cookie', setCookie);
|
|
202
202
|
}
|
|
203
203
|
return _context2.a(2, resp);
|
|
204
204
|
}
|
|
@@ -251,8 +251,8 @@ function getResourcesHandler(i18next) {
|
|
|
251
251
|
languages = options.getParams(req)[options.lngParam || 'lng'] ? options.getParams(req)[options.lngParam || 'lng'].split(' ') : [];
|
|
252
252
|
namespaces = options.getParams(req)[options.nsParam || 'ns'] ? options.getParams(req)[options.nsParam || 'ns'].split(' ') : [];
|
|
253
253
|
}
|
|
254
|
-
languages = languages.filter(utils.
|
|
255
|
-
namespaces = namespaces.filter(utils.
|
|
254
|
+
languages = languages.filter(utils.isSafeLangIdentifier);
|
|
255
|
+
namespaces = namespaces.filter(utils.isSafeNsIdentifier);
|
|
256
256
|
namespaces.forEach(function (ns) {
|
|
257
257
|
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) {
|
|
258
258
|
i18next.options.ns.push(ns);
|
package/cjs/utils.js
CHANGED
|
@@ -8,20 +8,31 @@ exports.defaults = defaults;
|
|
|
8
8
|
exports.escape = escape;
|
|
9
9
|
exports.extend = extend;
|
|
10
10
|
exports.hasXSS = hasXSS;
|
|
11
|
-
exports.isSafeIdentifier =
|
|
11
|
+
exports.isSafeIdentifier = void 0;
|
|
12
|
+
exports.isSafeLangIdentifier = isSafeLangIdentifier;
|
|
13
|
+
exports.isSafeNsIdentifier = isSafeNsIdentifier;
|
|
12
14
|
exports.removeLngFromUrl = removeLngFromUrl;
|
|
13
15
|
exports.sanitizeHeaderValue = sanitizeHeaderValue;
|
|
14
16
|
exports.setPath = setPath;
|
|
15
17
|
var UNSAFE_KEYS = exports.UNSAFE_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
16
|
-
function
|
|
18
|
+
function isSafeIdentifierBase(v) {
|
|
17
19
|
if (typeof v !== 'string') return false;
|
|
18
20
|
if (v.length === 0 || v.length > 128) return false;
|
|
19
21
|
if (UNSAFE_KEYS.indexOf(v) > -1) return false;
|
|
20
22
|
if (v.indexOf('..') > -1) return false;
|
|
21
|
-
if (v.indexOf('
|
|
23
|
+
if (v.indexOf('\\') > -1) return false;
|
|
22
24
|
if (/[\x00-\x1F\x7F]/.test(v)) return false;
|
|
23
25
|
return true;
|
|
24
26
|
}
|
|
27
|
+
function isSafeLangIdentifier(v) {
|
|
28
|
+
if (!isSafeIdentifierBase(v)) return false;
|
|
29
|
+
if (v.indexOf('/') > -1) return false;
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
function isSafeNsIdentifier(v) {
|
|
33
|
+
return isSafeIdentifierBase(v);
|
|
34
|
+
}
|
|
35
|
+
var isSafeIdentifier = exports.isSafeIdentifier = isSafeLangIdentifier;
|
|
25
36
|
function setPath(object, path, newValue) {
|
|
26
37
|
var stack;
|
|
27
38
|
if (typeof path !== 'string') stack = [].concat(path);
|
package/esm/index.js
CHANGED
|
@@ -181,7 +181,7 @@ export function freshPlugin(i18next, options) {
|
|
|
181
181
|
}
|
|
182
182
|
setCookie = placeholder.headers.get('Set-Cookie');
|
|
183
183
|
if (setCookie) {
|
|
184
|
-
resp.headers.
|
|
184
|
+
resp.headers.append('Set-Cookie', setCookie);
|
|
185
185
|
}
|
|
186
186
|
return _context2.a(2, resp);
|
|
187
187
|
}
|
|
@@ -234,8 +234,8 @@ export function getResourcesHandler(i18next) {
|
|
|
234
234
|
languages = options.getParams(req)[options.lngParam || 'lng'] ? options.getParams(req)[options.lngParam || 'lng'].split(' ') : [];
|
|
235
235
|
namespaces = options.getParams(req)[options.nsParam || 'ns'] ? options.getParams(req)[options.nsParam || 'ns'].split(' ') : [];
|
|
236
236
|
}
|
|
237
|
-
languages = languages.filter(utils.
|
|
238
|
-
namespaces = namespaces.filter(utils.
|
|
237
|
+
languages = languages.filter(utils.isSafeLangIdentifier);
|
|
238
|
+
namespaces = namespaces.filter(utils.isSafeNsIdentifier);
|
|
239
239
|
namespaces.forEach(function (ns) {
|
|
240
240
|
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) {
|
|
241
241
|
i18next.options.ns.push(ns);
|
package/esm/utils.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
export var UNSAFE_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
2
|
-
|
|
2
|
+
function isSafeIdentifierBase(v) {
|
|
3
3
|
if (typeof v !== 'string') return false;
|
|
4
4
|
if (v.length === 0 || v.length > 128) return false;
|
|
5
5
|
if (UNSAFE_KEYS.indexOf(v) > -1) return false;
|
|
6
6
|
if (v.indexOf('..') > -1) return false;
|
|
7
|
-
if (v.indexOf('
|
|
7
|
+
if (v.indexOf('\\') > -1) return false;
|
|
8
8
|
if (/[\x00-\x1F\x7F]/.test(v)) return false;
|
|
9
9
|
return true;
|
|
10
10
|
}
|
|
11
|
+
export function isSafeLangIdentifier(v) {
|
|
12
|
+
if (!isSafeIdentifierBase(v)) return false;
|
|
13
|
+
if (v.indexOf('/') > -1) return false;
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
export function isSafeNsIdentifier(v) {
|
|
17
|
+
return isSafeIdentifierBase(v);
|
|
18
|
+
}
|
|
19
|
+
export var isSafeIdentifier = isSafeLangIdentifier;
|
|
11
20
|
export function setPath(object, path, newValue) {
|
|
12
21
|
var stack;
|
|
13
22
|
if (typeof path !== 'string') stack = [].concat(path);
|
package/lib/index.js
CHANGED
|
@@ -170,7 +170,7 @@ export function freshPlugin (i18next, options) {
|
|
|
170
170
|
|
|
171
171
|
const setCookie = placeholder.headers.get('Set-Cookie')
|
|
172
172
|
if (setCookie) {
|
|
173
|
-
resp.headers.
|
|
173
|
+
resp.headers.append('Set-Cookie', setCookie)
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
return resp
|
|
@@ -258,8 +258,12 @@ export function getResourcesHandler (i18next, options = {}) {
|
|
|
258
258
|
// connector. i18next itself permits arbitrary language codes, so we do
|
|
259
259
|
// not impose a BCP-47 shape — we only block known-dangerous patterns.
|
|
260
260
|
// See: https://www.i18next.com/how-to/faq#how-should-the-language-codes-be-formatted
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
// lng goes through the strict check (rejects `/`); ns goes through
|
|
262
|
+
// the loose check (allows `/` so nested-namespace layouts like `a/b`
|
|
263
|
+
// that map to `public/locales/en/a/b.json` keep working). Directory
|
|
264
|
+
// escape via `..` / `\` / control chars is still blocked on both.
|
|
265
|
+
languages = languages.filter(utils.isSafeLangIdentifier)
|
|
266
|
+
namespaces = namespaces.filter(utils.isSafeNsIdentifier)
|
|
263
267
|
|
|
264
268
|
// extend ns
|
|
265
269
|
namespaces.forEach(ns => {
|
package/lib/utils.js
CHANGED
|
@@ -1,25 +1,48 @@
|
|
|
1
1
|
export const UNSAFE_KEYS = ['__proto__', 'constructor', 'prototype']
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
// - no control characters (header injection downstream, log forging)
|
|
11
|
-
// - non-empty, length <= 128
|
|
12
|
-
export function isSafeIdentifier (v) {
|
|
3
|
+
// Shared denylist: the patterns that are always unsafe to forward to a
|
|
4
|
+
// backend connector, regardless of whether the value is a language code
|
|
5
|
+
// or a namespace name. Blocks directory escape (`..`), Windows path
|
|
6
|
+
// separators (`\`), control characters, prototype keys, empty strings,
|
|
7
|
+
// and oversized inputs. The one dimension that differs per-key is whether
|
|
8
|
+
// `/` is allowed — see the two per-key helpers below.
|
|
9
|
+
function isSafeIdentifierBase (v) {
|
|
13
10
|
if (typeof v !== 'string') return false
|
|
14
11
|
if (v.length === 0 || v.length > 128) return false
|
|
15
12
|
if (UNSAFE_KEYS.indexOf(v) > -1) return false
|
|
16
13
|
if (v.indexOf('..') > -1) return false
|
|
17
|
-
if (v.indexOf('
|
|
14
|
+
if (v.indexOf('\\') > -1) return false
|
|
18
15
|
// eslint-disable-next-line no-control-regex
|
|
19
16
|
if (/[\x00-\x1F\x7F]/.test(v)) return false
|
|
20
17
|
return true
|
|
21
18
|
}
|
|
22
19
|
|
|
20
|
+
// Strict — also rejects `/`. Applied to `lng`:
|
|
21
|
+
// no legitimate BCP-47 / i18next language-code shape
|
|
22
|
+
// (https://www.i18next.com/how-to/faq#how-should-the-language-codes-be-formatted)
|
|
23
|
+
// contains `/`, so allowing it would only enable attacks.
|
|
24
|
+
export function isSafeLangIdentifier (v) {
|
|
25
|
+
if (!isSafeIdentifierBase(v)) return false
|
|
26
|
+
if (v.indexOf('/') > -1) return false
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Loose — allows `/`. Applied to `ns`:
|
|
31
|
+
// nested namespace names like `a/b` that map to subfolder locale layouts
|
|
32
|
+
// (`public/locales/en/a/b.json` on fs-backend, `/locales/en/a/b.json`
|
|
33
|
+
// on http-backend) are a documented i18next pattern and were rejected
|
|
34
|
+
// in 3.9.3 as an unintended regression. Directory escape is still
|
|
35
|
+
// prevented — `..` is still blocked, `\` is still blocked — so the
|
|
36
|
+
// security fix remains in force for every concrete attack pattern
|
|
37
|
+
// from the original advisory.
|
|
38
|
+
export function isSafeNsIdentifier (v) {
|
|
39
|
+
return isSafeIdentifierBase(v)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Backwards-compatible alias for the strict check. Kept because 3.9.3
|
|
43
|
+
// exported this symbol; external callers (if any) get the pre-fix behaviour.
|
|
44
|
+
export const isSafeIdentifier = isSafeLangIdentifier
|
|
45
|
+
|
|
23
46
|
export function setPath (object, path, newValue) {
|
|
24
47
|
let stack
|
|
25
48
|
if (typeof path !== 'string') stack = [].concat(path)
|