i18next-http-middleware 3.7.2 → 3.7.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 +4 -0
- package/cjs/LanguageDetector.js +2 -3
- package/cjs/index.js +4 -0
- package/cjs/utils.js +8 -0
- package/esm/LanguageDetector.js +2 -3
- package/esm/index.js +4 -0
- package/esm/utils.js +7 -0
- package/lib/LanguageDetector.js +3 -2
- package/lib/index.js +4 -0
- package/lib/utils.js +23 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ app.use(
|
|
|
43
43
|
|
|
44
44
|
// in your request handler
|
|
45
45
|
app.get('myRoute', (req, res) => {
|
|
46
|
+
var resolvedLng = req.resolvedLanguage // 'de-CH'
|
|
46
47
|
var lng = req.language // 'de-CH'
|
|
47
48
|
var lngs = req.languages // ['de-CH', 'de', 'en']
|
|
48
49
|
req.i18n.changeLanguage('en') // will not load that!!! assert it was preloaded
|
|
@@ -117,6 +118,7 @@ server.route({
|
|
|
117
118
|
method: 'GET',
|
|
118
119
|
path: '/myRoute',
|
|
119
120
|
handler: (request, h) => {
|
|
121
|
+
var resolvedLng = request.resolvedLanguage // 'de-CH'
|
|
120
122
|
var lng = request.language // 'de-CH'
|
|
121
123
|
var lngs = v.languages // ['de-CH', 'de', 'en']
|
|
122
124
|
request.i18n.changeLanguage('en') // will not load that!!! assert it was preloaded
|
|
@@ -149,7 +151,9 @@ app.use(i18nextMiddleware.koaPlugin(i18next, {
|
|
|
149
151
|
// in your request handler
|
|
150
152
|
router.get('/myRoute', ctx => {
|
|
151
153
|
ctx.body = JSON.stringify({
|
|
154
|
+
'ctx.resolvedLanguage': ctx.resolvedLanguage,
|
|
152
155
|
'ctx.language': ctx.language,
|
|
156
|
+
'ctx.i18n.resolvedLanguage': ctx.i18n.resolvedLanguage,
|
|
153
157
|
'ctx.i18n.language': ctx.i18n.language,
|
|
154
158
|
'ctx.i18n.languages': ctx.i18n.languages,
|
|
155
159
|
'ctx.i18n.languages[0]': ctx.i18n.languages[0],
|
package/cjs/LanguageDetector.js
CHANGED
|
@@ -81,9 +81,8 @@ var LanguageDetector = function () {
|
|
|
81
81
|
if (!detections) return;
|
|
82
82
|
if (!Array.isArray(detections)) detections = [detections];
|
|
83
83
|
detections = detections.filter(function (d) {
|
|
84
|
-
return d !== undefined && d !== null;
|
|
85
|
-
})
|
|
86
|
-
detections = detections.map(function (d) {
|
|
84
|
+
return d !== undefined && d !== null && !utils.hasXSS(d);
|
|
85
|
+
}).map(function (d) {
|
|
87
86
|
return _this.options.convertDetectedLanguage(d);
|
|
88
87
|
});
|
|
89
88
|
if (_this.services.languageUtils.getBestMatchFromCodes) {
|
package/cjs/index.js
CHANGED
|
@@ -80,11 +80,13 @@ function handle(i18next) {
|
|
|
80
80
|
if (res.locals) {
|
|
81
81
|
res.locals.language = lng;
|
|
82
82
|
res.locals.languageDir = i18next.dir(lng);
|
|
83
|
+
res.locals.resolvedLanguage = i18n.resolvedLanguage;
|
|
83
84
|
}
|
|
84
85
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
85
86
|
options.setHeader(res, 'Content-Language', utils.escape(lng));
|
|
86
87
|
}
|
|
87
88
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
|
|
89
|
+
req.resolvedLanguage = i18n.resolvedLanguage;
|
|
88
90
|
if (i18next.services.languageDetector) {
|
|
89
91
|
i18next.services.languageDetector.cacheUserLanguage(req, res, lng);
|
|
90
92
|
}
|
|
@@ -98,6 +100,7 @@ function handle(i18next) {
|
|
|
98
100
|
options.setHeader(res, 'Content-Language', utils.escape(lng));
|
|
99
101
|
}
|
|
100
102
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
|
|
103
|
+
req.resolvedLanguage = i18n.resolvedLanguage;
|
|
101
104
|
i18n.changeLanguage(lng || i18next.options.fallbackLng[0]);
|
|
102
105
|
if (req.i18nextLookupName === 'path' && options.removeLngFromUrl) {
|
|
103
106
|
options.setUrl(req, utils.removeLngFromUrl(options.getUrl(req), i18next.services.languageDetector.options.lookupFromPathIndex));
|
|
@@ -112,6 +115,7 @@ function handle(i18next) {
|
|
|
112
115
|
res.locals.exists = exists;
|
|
113
116
|
res.locals.i18n = i18n;
|
|
114
117
|
res.locals.language = lng;
|
|
118
|
+
res.locals.resolvedLanguage = i18n.resolvedLanguage;
|
|
115
119
|
res.locals.languageDir = i18next.dir(lng);
|
|
116
120
|
}
|
|
117
121
|
if (i18next.services.languageDetector) {
|
package/cjs/utils.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.defaults = defaults;
|
|
7
7
|
exports.escape = escape;
|
|
8
8
|
exports.extend = extend;
|
|
9
|
+
exports.hasXSS = hasXSS;
|
|
9
10
|
exports.removeLngFromUrl = removeLngFromUrl;
|
|
10
11
|
exports.setPath = setPath;
|
|
11
12
|
function setPath(object, path, newValue) {
|
|
@@ -60,4 +61,11 @@ function removeLngFromUrl(url, lookupFromPathIndex) {
|
|
|
60
61
|
}
|
|
61
62
|
function escape(str) {
|
|
62
63
|
return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>').replace(/\//g, '/').replace(/\\/g, '\').replace(/`/g, '`');
|
|
64
|
+
}
|
|
65
|
+
function hasXSS(input) {
|
|
66
|
+
if (typeof input !== 'string') return false;
|
|
67
|
+
var xssPatterns = [/<\s*script.*?>/i, /<\s*\/\s*script\s*>/i, /<\s*img.*?on\w+\s*=/i, /<\s*\w+\s*on\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
|
+
return xssPatterns.some(function (pattern) {
|
|
69
|
+
return pattern.test(input);
|
|
70
|
+
});
|
|
63
71
|
}
|
package/esm/LanguageDetector.js
CHANGED
|
@@ -72,9 +72,8 @@ var LanguageDetector = function () {
|
|
|
72
72
|
if (!detections) return;
|
|
73
73
|
if (!Array.isArray(detections)) detections = [detections];
|
|
74
74
|
detections = detections.filter(function (d) {
|
|
75
|
-
return d !== undefined && d !== null;
|
|
76
|
-
})
|
|
77
|
-
detections = detections.map(function (d) {
|
|
75
|
+
return d !== undefined && d !== null && !utils.hasXSS(d);
|
|
76
|
+
}).map(function (d) {
|
|
78
77
|
return _this.options.convertDetectedLanguage(d);
|
|
79
78
|
});
|
|
80
79
|
if (_this.services.languageUtils.getBestMatchFromCodes) {
|
package/esm/index.js
CHANGED
|
@@ -63,11 +63,13 @@ export function handle(i18next) {
|
|
|
63
63
|
if (res.locals) {
|
|
64
64
|
res.locals.language = lng;
|
|
65
65
|
res.locals.languageDir = i18next.dir(lng);
|
|
66
|
+
res.locals.resolvedLanguage = i18n.resolvedLanguage;
|
|
66
67
|
}
|
|
67
68
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
68
69
|
options.setHeader(res, 'Content-Language', utils.escape(lng));
|
|
69
70
|
}
|
|
70
71
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
|
|
72
|
+
req.resolvedLanguage = i18n.resolvedLanguage;
|
|
71
73
|
if (i18next.services.languageDetector) {
|
|
72
74
|
i18next.services.languageDetector.cacheUserLanguage(req, res, lng);
|
|
73
75
|
}
|
|
@@ -81,6 +83,7 @@ export function handle(i18next) {
|
|
|
81
83
|
options.setHeader(res, 'Content-Language', utils.escape(lng));
|
|
82
84
|
}
|
|
83
85
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
|
|
86
|
+
req.resolvedLanguage = i18n.resolvedLanguage;
|
|
84
87
|
i18n.changeLanguage(lng || i18next.options.fallbackLng[0]);
|
|
85
88
|
if (req.i18nextLookupName === 'path' && options.removeLngFromUrl) {
|
|
86
89
|
options.setUrl(req, utils.removeLngFromUrl(options.getUrl(req), i18next.services.languageDetector.options.lookupFromPathIndex));
|
|
@@ -95,6 +98,7 @@ export function handle(i18next) {
|
|
|
95
98
|
res.locals.exists = exists;
|
|
96
99
|
res.locals.i18n = i18n;
|
|
97
100
|
res.locals.language = lng;
|
|
101
|
+
res.locals.resolvedLanguage = i18n.resolvedLanguage;
|
|
98
102
|
res.locals.languageDir = i18next.dir(lng);
|
|
99
103
|
}
|
|
100
104
|
if (i18next.services.languageDetector) {
|
package/esm/utils.js
CHANGED
|
@@ -50,4 +50,11 @@ export function removeLngFromUrl(url, lookupFromPathIndex) {
|
|
|
50
50
|
}
|
|
51
51
|
export function escape(str) {
|
|
52
52
|
return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>').replace(/\//g, '/').replace(/\\/g, '\').replace(/`/g, '`');
|
|
53
|
+
}
|
|
54
|
+
export function hasXSS(input) {
|
|
55
|
+
if (typeof input !== 'string') return false;
|
|
56
|
+
var xssPatterns = [/<\s*script.*?>/i, /<\s*\/\s*script\s*>/i, /<\s*img.*?on\w+\s*=/i, /<\s*\w+\s*on\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
|
+
return xssPatterns.some(function (pattern) {
|
|
58
|
+
return pattern.test(input);
|
|
59
|
+
});
|
|
53
60
|
}
|
package/lib/LanguageDetector.js
CHANGED
|
@@ -66,8 +66,9 @@ class LanguageDetector {
|
|
|
66
66
|
if (!detections) return
|
|
67
67
|
if (!Array.isArray(detections)) detections = [detections]
|
|
68
68
|
|
|
69
|
-
detections = detections
|
|
70
|
-
|
|
69
|
+
detections = detections
|
|
70
|
+
.filter((d) => d !== undefined && d !== null && !utils.hasXSS(d))
|
|
71
|
+
.map((d) => this.options.convertDetectedLanguage(d))
|
|
71
72
|
|
|
72
73
|
if (this.services.languageUtils.getBestMatchFromCodes) { // new i18next v19.5.0
|
|
73
74
|
found = this.services.languageUtils.getBestMatchFromCodes(detections)
|
package/lib/index.js
CHANGED
|
@@ -52,6 +52,7 @@ export function handle (i18next, options = {}) {
|
|
|
52
52
|
if (res.locals) {
|
|
53
53
|
res.locals.language = lng
|
|
54
54
|
res.locals.languageDir = i18next.dir(lng)
|
|
55
|
+
res.locals.resolvedLanguage = i18n.resolvedLanguage
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
@@ -59,6 +60,7 @@ export function handle (i18next, options = {}) {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng)
|
|
63
|
+
req.resolvedLanguage = i18n.resolvedLanguage
|
|
62
64
|
|
|
63
65
|
if (i18next.services.languageDetector) {
|
|
64
66
|
i18next.services.languageDetector.cacheUserLanguage(req, res, lng)
|
|
@@ -76,6 +78,7 @@ export function handle (i18next, options = {}) {
|
|
|
76
78
|
options.setHeader(res, 'Content-Language', utils.escape(lng))
|
|
77
79
|
}
|
|
78
80
|
req.languages = i18next.services.languageUtils.toResolveHierarchy(lng)
|
|
81
|
+
req.resolvedLanguage = i18n.resolvedLanguage
|
|
79
82
|
|
|
80
83
|
// trigger sync to instance - might trigger async load!
|
|
81
84
|
i18n.changeLanguage(lng || i18next.options.fallbackLng[0])
|
|
@@ -104,6 +107,7 @@ export function handle (i18next, options = {}) {
|
|
|
104
107
|
res.locals.exists = exists
|
|
105
108
|
res.locals.i18n = i18n
|
|
106
109
|
res.locals.language = lng
|
|
110
|
+
res.locals.resolvedLanguage = i18n.resolvedLanguage
|
|
107
111
|
res.locals.languageDir = i18next.dir(lng)
|
|
108
112
|
}
|
|
109
113
|
|
package/lib/utils.js
CHANGED
|
@@ -69,3 +69,26 @@ export function escape (str) {
|
|
|
69
69
|
.replace(/\\/g, '\')
|
|
70
70
|
.replace(/`/g, '`'))
|
|
71
71
|
}
|
|
72
|
+
|
|
73
|
+
export function hasXSS (input) {
|
|
74
|
+
if (typeof input !== 'string') return false
|
|
75
|
+
|
|
76
|
+
// Common XSS attack patterns
|
|
77
|
+
const xssPatterns = [
|
|
78
|
+
/<\s*script.*?>/i,
|
|
79
|
+
/<\s*\/\s*script\s*>/i,
|
|
80
|
+
/<\s*img.*?on\w+\s*=/i,
|
|
81
|
+
/<\s*\w+\s*on\w+\s*=.*?>/i,
|
|
82
|
+
/javascript\s*:/i,
|
|
83
|
+
/vbscript\s*:/i,
|
|
84
|
+
/expression\s*\(/i,
|
|
85
|
+
/eval\s*\(/i,
|
|
86
|
+
/alert\s*\(/i,
|
|
87
|
+
/document\.cookie/i,
|
|
88
|
+
/document\.write\s*\(/i,
|
|
89
|
+
/window\.location/i,
|
|
90
|
+
/innerHTML/i
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
return xssPatterns.some((pattern) => pattern.test(input))
|
|
94
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18next-http-middleware",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./cjs/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@hapi/hapi": "^21.3.12",
|
|
35
35
|
"@types/express-serve-static-core": "^5.0.1",
|
|
36
36
|
"@koa/router": "12.0.1",
|
|
37
|
-
"koa": "2.
|
|
37
|
+
"koa": "2.16.1",
|
|
38
38
|
"babel-plugin-add-module-exports": "1.0.4",
|
|
39
39
|
"eslint": "8.53.0",
|
|
40
40
|
"eslint-config-standard": "17.1.0",
|