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 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],
@@ -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, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\//g, '&#x2F;').replace(/\\/g, '&#x5C;').replace(/`/g, '&#96;');
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
  }
@@ -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, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\//g, '&#x2F;').replace(/\\/g, '&#x5C;').replace(/`/g, '&#96;');
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
  }
@@ -66,8 +66,9 @@ class LanguageDetector {
66
66
  if (!detections) return
67
67
  if (!Array.isArray(detections)) detections = [detections]
68
68
 
69
- detections = detections.filter((d) => d !== undefined && d !== null)
70
- detections = detections.map((d) => this.options.convertDetectedLanguage(d))
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, '&#x5C;')
70
70
  .replace(/`/g, '&#96;'))
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.2",
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.15.3",
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",