Socket
Socket
Sign inDemoInstall

fastify

Package Overview
Dependencies
Maintainers
3
Versions
288
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fastify - npm Package Compare versions

Comparing version 3.29.3 to 3.29.4

2

fastify.js
'use strict'
const VERSION = '3.29.3'
const VERSION = '3.29.4'

@@ -5,0 +5,0 @@ const Avvio = require('avvio')

@@ -9,2 +9,3 @@ 'use strict'

lru = typeof lru === 'function' ? lru : lru.default
const { parse: parseContentType } = require('content-type')

@@ -40,3 +41,3 @@ const secureJson = require('secure-json-parse')

this.customParsers.set('text/plain', new Parser(true, false, bodyLimit, defaultPlainTextParser))
this.parserList = ['application/json', 'text/plain']
this.parserList = [new ParserListItem('application/json'), new ParserListItem('text/plain')]
this.parserRegExpList = []

@@ -74,3 +75,3 @@ this.cache = lru(100)

if (contentTypeIsString) {
this.parserList.unshift(contentType)
this.parserList.unshift(new ParserListItem(contentType))
} else {

@@ -103,7 +104,21 @@ this.parserRegExpList.unshift(contentType)

const parser = this.cache.get(contentType)
// TODO not covered by tests, this is a security backport
/* istanbul ignore next */
if (parser !== undefined) return parser
const parsed = safeParseContentType(contentType)
// dummyContentType always the same object
// we can use === for the comparsion and return early
if (parsed === dummyContentType) {
return this.customParsers.get('')
}
// eslint-disable-next-line no-var
for (var i = 0; i !== this.parserList.length; ++i) {
const parserName = this.parserList[i]
if (contentType.indexOf(parserName) !== -1) {
const parser = this.customParsers.get(parserName)
const parserListItem = this.parserList[i]
if (compareContentType(parsed, parserListItem)) {
const parser = this.customParsers.get(parserListItem.name)
// we set request content-type in cache to reduce parsing of MIME type
this.cache.set(contentType, parser)

@@ -117,4 +132,5 @@ return parser

const parserRegExp = this.parserRegExpList[j]
if (parserRegExp.test(contentType)) {
if (compareRegExpContentType(contentType, parsed.type, parserRegExp)) {
const parser = this.customParsers.get(parserRegExp.toString())
// we set request content-type in cache to reduce parsing of MIME type
this.cache.set(contentType, parser)

@@ -356,2 +372,59 @@ return parser

// dummy here to prevent repeated object creation
const dummyContentType = { type: '', parameters: Object.create(null) }
function safeParseContentType (contentType) {
try {
return parseContentType(contentType)
} catch (err) {
return dummyContentType
}
}
function compareContentType (contentType, parserListItem) {
if (parserListItem.isEssence) {
// we do essence check
return contentType.type.indexOf(parserListItem) !== -1
} else {
// when the content-type includes parameters
// we do a full-text search
// reject essence content-type before checking parameters
if (contentType.type.indexOf(parserListItem.type) === -1) return false
for (const key of parserListItem.parameterKeys) {
// reject when missing parameters
if (!(key in contentType.parameters)) return false
// reject when parameters do not match
if (contentType.parameters[key] !== parserListItem.parameters[key]) return false
}
return true
}
}
function compareRegExpContentType (contentType, essenceMIMEType, regexp) {
if (regexp.source.indexOf(';') === -1) {
// we do essence check
return regexp.test(essenceMIMEType)
} else {
// when the content-type includes parameters
// we do a full-text match
return regexp.test(contentType)
}
}
function ParserListItem (contentType) {
this.name = contentType
// we pre-calculate all the needed information
// before content-type comparsion
const parsed = safeParseContentType(contentType)
this.type = parsed.type
this.parameters = parsed.parameters
this.parameterKeys = Object.keys(parsed.parameters)
this.isEssence = contentType.indexOf(';') === -1
}
// used in ContentTypeParser.remove
ParserListItem.prototype.toString = function () {
return this.name
}
module.exports = ContentTypeParser

@@ -358,0 +431,0 @@ module.exports.helpers = {

{
"name": "fastify",
"version": "3.29.3",
"version": "3.29.4",
"description": "Fast and low overhead web framework, for Node.js",

@@ -190,2 +190,3 @@ "main": "fastify.js",

"process-warning": "^1.0.0",
"content-type": "^1.0.4",
"proxy-addr": "^2.0.7",

@@ -192,0 +193,0 @@ "rfdc": "^1.1.4",

@@ -331,1 +331,215 @@ 'use strict'

})
test('Safeguard against content-type spoofing - string', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/plain', function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.addContentTypeParser('application/json', function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'text/plain; content-type="application/json"'
},
body: ''
})
})
test('Safeguard against content-type spoofing - regexp', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/text\/plain/, function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.addContentTypeParser(/application\/json/, function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'text/plain; content-type="application/json"'
},
body: ''
})
})
test('content-type match parameters - string 1', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/plain; charset=utf8', function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.addContentTypeParser('application/json; charset=utf8', function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8'
},
body: ''
})
})
test('content-type match parameters - string 2', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.addContentTypeParser('text/plain; charset=utf8; foo=bar', function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; foo=bar; charset=utf8'
},
body: ''
})
})
test('content-type match parameters - regexp', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/application\/json; charset=utf8/, function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8'
},
body: ''
})
})
test('content-type fail when parameters not match - string 1', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8'
},
body: ''
})
t.same(response.statusCode, 415)
})
test('content-type fail when parameters not match - string 2', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8; foo=baz'
},
body: ''
})
t.same(response.statusCode, 415)
})
test('content-type fail when parameters not match - regexp', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/application\/json; charset=utf8; foo=bar/, function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8'
},
body: ''
})
t.same(response.statusCode, 415)
})

@@ -1123,3 +1123,3 @@ 'use strict'

headers: {
'Content-Type': 'application/json charset=utf-8'
'Content-Type': 'application/json; charset=utf-8'
}

@@ -1307,3 +1307,3 @@ }, (err, response, body) => {

headers: {
'Content-Type': 'weird-content-type+json'
'Content-Type': 'weird/content-type+json'
}

@@ -1338,3 +1338,3 @@ }, (err, response, body) => {

fastify.addContentTypeParser(/.*\+myExtension$/, function (req, payload, done) {
fastify.addContentTypeParser(/.*\+myExtension$/i, function (req, payload, done) {
let data = ''

@@ -1341,0 +1341,0 @@ payload.on('data', chunk => { data += chunk })

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc