diff --git a/index.js b/index.js index e607a923..e7ba7cfb 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ const fp = require('fastify-plugin') const { lru } = require('tiny-lru') const querystring = require('fast-querystring') +const fastContentTypeParse = require('fast-content-type-parse') const Stream = require('node:stream') const buildRequest = require('./lib/request') const { @@ -107,15 +108,13 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) { body = this.request.body } else { // Per RFC 7231 ยง3.1.1.5 if this header is not present we MAY assume application/octet-stream - const contentType = req.headers['content-type'] || 'application/octet-stream' - // detect if body should be encoded as JSON - // supporting extended content-type header formats: - // - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type - const lowerCaseContentType = contentType.toLowerCase() - const plainContentType = lowerCaseContentType.indexOf(';') > -1 - ? lowerCaseContentType.slice(0, lowerCaseContentType.indexOf(';')) - : lowerCaseContentType - const shouldEncodeJSON = contentTypesToEncode.has(plainContentType) + let contentType = 'application/octet-stream' + if (req.headers['content-type']) { + const plainContentType = fastContentTypeParse.parse(req.headers['content-type']) + contentType = plainContentType.type + } + + const shouldEncodeJSON = contentTypesToEncode.has(contentType) // transparently support JSON encoding body = shouldEncodeJSON ? JSON.stringify(this.request.body) : this.request.body // update origin request headers after encoding diff --git a/package.json b/package.json index 0168c556..51b3e495 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "dependencies": { "@fastify/error": "^3.0.0", "end-of-stream": "^1.4.4", + "fast-content-type-parse": "^1.1.0", "fast-querystring": "^1.0.0", "fastify-plugin": "^4.0.0", "pump": "^3.0.0", diff --git a/test/fix-GHSA-v2v2-hph8-q5xp.test.js b/test/fix-GHSA-v2v2-hph8-q5xp.test.js new file mode 100644 index 00000000..59f37d5c --- /dev/null +++ b/test/fix-GHSA-v2v2-hph8-q5xp.test.js @@ -0,0 +1,47 @@ +'use strict' + +const t = require('tap') +const fastify = require('fastify') +const get = require('simple-get').concat +const From = require('..') + +const upstream = fastify() +t.teardown(upstream.close.bind(upstream)) +t.plan(4) + +upstream.post('/test', async (request, reply) => { + if (typeof request.body === 'object') { + return 'not ok' + } + return 'ok' +}) + +upstream.listen({ port: 0 }, function (err) { + t.error(err) + + const app = fastify() + app.register(From) + t.teardown(app.close.bind(app)) + + app.post('/test', (request, reply) => { + if (request.body.method === 'invalid_method') { + return reply.code(400).send({ message: 'payload contains invalid method' }) + } + reply.from(`http://127.0.0.1:${upstream.server.address().port}/test`) + }) + + app.listen({ port: 0 }, function (err) { + t.error(err) + + get({ + url: `http://127.0.0.1:${app.server.address().port}/test`, + headers: { 'content-type': 'application/json ; charset=utf-8' }, + // eslint-disable-next-line no-useless-escape + body: '"{\\\"method\\\":\\\"invalid_method\\\"}"', + method: 'POST' + }, (err, res, data) => { + t.error(err) + t.equal(data.toString(), 'ok') + }) + }) +})