fastify 4.8.0 → 4.8.1

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/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '4.8.0'
3
+ const VERSION = '4.8.1'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
@@ -29,9 +29,10 @@ const {
29
29
 
30
30
  function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
31
31
  this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
32
- this.customParsers = {}
33
- this.customParsers['application/json'] = new Parser(true, false, bodyLimit, this[kDefaultJsonParse])
34
- this.customParsers['text/plain'] = new Parser(true, false, bodyLimit, defaultPlainTextParser)
32
+ // using a map instead of a plain object to avoid prototype hijack attacks
33
+ this.customParsers = new Map()
34
+ this.customParsers.set('application/json', new Parser(true, false, bodyLimit, this[kDefaultJsonParse]))
35
+ this.customParsers.set('text/plain', new Parser(true, false, bodyLimit, defaultPlainTextParser))
35
36
  this.parserList = ['application/json', 'text/plain']
36
37
  this.parserRegExpList = []
37
38
  this.cache = lru(100)
@@ -62,35 +63,35 @@ ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
62
63
  )
63
64
 
64
65
  if (contentTypeIsString && contentType === '*') {
65
- this.customParsers[''] = parser
66
+ this.customParsers.set('', parser)
66
67
  } else {
67
68
  if (contentTypeIsString) {
68
69
  this.parserList.unshift(contentType)
69
70
  } else {
70
71
  this.parserRegExpList.unshift(contentType)
71
72
  }
72
- this.customParsers[contentType] = parser
73
+ this.customParsers.set(contentType.toString(), parser)
73
74
  }
74
75
  }
75
76
 
76
77
  ContentTypeParser.prototype.hasParser = function (contentType) {
77
- return contentType in this.customParsers
78
+ return this.customParsers.has(typeof contentType === 'string' ? contentType : contentType.toString())
78
79
  }
79
80
 
80
81
  ContentTypeParser.prototype.existingParser = function (contentType) {
81
- if (contentType === 'application/json') {
82
- return this.customParsers['application/json'] && this.customParsers['application/json'].fn !== this[kDefaultJsonParse]
82
+ if (contentType === 'application/json' && this.customParsers.has(contentType)) {
83
+ return this.customParsers.get(contentType).fn !== this[kDefaultJsonParse]
83
84
  }
84
- if (contentType === 'text/plain') {
85
- return this.customParsers['text/plain'] && this.customParsers['text/plain'].fn !== defaultPlainTextParser
85
+ if (contentType === 'text/plain' && this.customParsers.has(contentType)) {
86
+ return this.customParsers.get(contentType).fn !== defaultPlainTextParser
86
87
  }
87
88
 
88
- return contentType in this.customParsers
89
+ return this.hasParser(contentType)
89
90
  }
90
91
 
91
92
  ContentTypeParser.prototype.getParser = function (contentType) {
92
- if (contentType in this.customParsers) {
93
- return this.customParsers[contentType]
93
+ if (this.hasParser(contentType)) {
94
+ return this.customParsers.get(contentType)
94
95
  }
95
96
 
96
97
  if (this.cache.has(contentType)) {
@@ -101,7 +102,7 @@ ContentTypeParser.prototype.getParser = function (contentType) {
101
102
  for (var i = 0; i !== this.parserList.length; ++i) {
102
103
  const parserName = this.parserList[i]
103
104
  if (contentType.indexOf(parserName) !== -1) {
104
- const parser = this.customParsers[parserName]
105
+ const parser = this.customParsers.get(parserName)
105
106
  this.cache.set(contentType, parser)
106
107
  return parser
107
108
  }
@@ -111,17 +112,17 @@ ContentTypeParser.prototype.getParser = function (contentType) {
111
112
  for (var j = 0; j !== this.parserRegExpList.length; ++j) {
112
113
  const parserRegExp = this.parserRegExpList[j]
113
114
  if (parserRegExp.test(contentType)) {
114
- const parser = this.customParsers[parserRegExp]
115
+ const parser = this.customParsers.get(parserRegExp.toString())
115
116
  this.cache.set(contentType, parser)
116
117
  return parser
117
118
  }
118
119
  }
119
120
 
120
- return this.customParsers['']
121
+ return this.customParsers.get('')
121
122
  }
122
123
 
123
124
  ContentTypeParser.prototype.removeAll = function () {
124
- this.customParsers = {}
125
+ this.customParsers = new Map()
125
126
  this.parserRegExpList = []
126
127
  this.parserList = []
127
128
  this.cache = lru(100)
@@ -130,7 +131,7 @@ ContentTypeParser.prototype.removeAll = function () {
130
131
  ContentTypeParser.prototype.remove = function (contentType) {
131
132
  if (!(typeof contentType === 'string' || contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
132
133
 
133
- delete this.customParsers[contentType]
134
+ this.customParsers.delete(contentType.toString())
134
135
 
135
136
  const parsers = typeof contentType === 'string' ? this.parserList : this.parserRegExpList
136
137
 
@@ -289,7 +290,7 @@ function Parser (asString, asBuffer, bodyLimit, fn) {
289
290
  function buildContentTypeParser (c) {
290
291
  const contentTypeParser = new ContentTypeParser()
291
292
  contentTypeParser[kDefaultJsonParse] = c[kDefaultJsonParse]
292
- Object.assign(contentTypeParser.customParsers, c.customParsers)
293
+ contentTypeParser.customParsers = new Map(c.customParsers.entries())
293
294
  contentTypeParser.parserList = c.parserList.slice()
294
295
  return contentTypeParser
295
296
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.8.0",
3
+ "version": "4.8.1",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -196,7 +196,7 @@ test('add', t => {
196
196
  const contentTypeParser = fastify[keys.kContentTypeParser]
197
197
 
198
198
  contentTypeParser.add('*', {}, first)
199
- t.equal(contentTypeParser.customParsers[''].fn, first)
199
+ t.equal(contentTypeParser.customParsers.get('').fn, first)
200
200
  })
201
201
 
202
202
  t.end()
@@ -306,7 +306,7 @@ test('remove', t => {
306
306
 
307
307
  contentTypeParser.remove('image/png')
308
308
 
309
- t.same(Object.keys(contentTypeParser.customParsers).length, 2)
309
+ t.same(contentTypeParser.customParsers.size, 2)
310
310
  })
311
311
 
312
312
  t.end()
@@ -329,3 +329,69 @@ test('remove all should remove all existing parsers and reset cache', t => {
329
329
  t.same(contentTypeParser.parserRegExpList.length, 0)
330
330
  t.same(Object.keys(contentTypeParser.customParsers).length, 0)
331
331
  })
332
+
333
+ test('Safeguard against malicious content-type / 1', async t => {
334
+ const badNames = Object.getOwnPropertyNames({}.__proto__) // eslint-disable-line
335
+ t.plan(badNames.length)
336
+
337
+ const fastify = Fastify()
338
+
339
+ fastify.post('/', async () => {
340
+ return 'ok'
341
+ })
342
+
343
+ for (const prop of badNames) {
344
+ const response = await fastify.inject({
345
+ method: 'POST',
346
+ path: '/',
347
+ headers: {
348
+ 'content-type': prop
349
+ },
350
+ body: ''
351
+ })
352
+
353
+ t.same(response.statusCode, 415)
354
+ }
355
+ })
356
+
357
+ test('Safeguard against malicious content-type / 2', async t => {
358
+ t.plan(1)
359
+
360
+ const fastify = Fastify()
361
+
362
+ fastify.post('/', async () => {
363
+ return 'ok'
364
+ })
365
+
366
+ const response = await fastify.inject({
367
+ method: 'POST',
368
+ path: '/',
369
+ headers: {
370
+ 'content-type': '\\u0063\\u006fnstructor'
371
+ },
372
+ body: ''
373
+ })
374
+
375
+ t.same(response.statusCode, 415)
376
+ })
377
+
378
+ test('Safeguard against malicious content-type / 3', async t => {
379
+ t.plan(1)
380
+
381
+ const fastify = Fastify()
382
+
383
+ fastify.post('/', async () => {
384
+ return 'ok'
385
+ })
386
+
387
+ const response = await fastify.inject({
388
+ method: 'POST',
389
+ path: '/',
390
+ headers: {
391
+ 'content-type': 'constructor; charset=utf-8'
392
+ },
393
+ body: ''
394
+ })
395
+
396
+ t.same(response.statusCode, 415)
397
+ })