From 9eb206b62dd97efa7338114e2597268bbed42190 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 16 Sep 2014 00:27:08 -0700 Subject: [PATCH 01/21] Default urlencoded extended option to false --- HISTORY.md | 5 +++++ README.md | 8 +++----- lib/types/urlencoded.js | 8 +------- test/urlencoded.js | 4 ++-- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 34e0d4c0..81928891 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +2.x +=== + + * `urlencoded` parser now defaults `extended` to `false` + 1.19.1 / 2021-12-10 =================== diff --git a/README.md b/README.md index 7d7fa882..6da044a0 100644 --- a/README.md +++ b/README.md @@ -237,9 +237,7 @@ encoded into the URL-encoded format, allowing for a JSON-like experience with URL-encoded. For more information, please [see the qs library](https://www.npmjs.org/package/qs#readme). -Defaults to `true`, but using the default has been deprecated. Please -research into the difference between `qs` and `querystring` and choose the -appropriate setting. +Defaults to `false`. ##### inflate @@ -380,7 +378,7 @@ var bodyParser = require('body-parser') var app = express() // parse application/x-www-form-urlencoded -app.use(bodyParser.urlencoded({ extended: false })) +app.use(bodyParser.urlencoded()) // parse application/json app.use(bodyParser.json()) @@ -408,7 +406,7 @@ var app = express() var jsonParser = bodyParser.json() // create application/x-www-form-urlencoded parser -var urlencodedParser = bodyParser.urlencoded({ extended: false }) +var urlencodedParser = bodyParser.urlencoded() // POST /login gets urlencoded bodies app.post('/login', urlencodedParser, function (req, res) { diff --git a/lib/types/urlencoded.js b/lib/types/urlencoded.js index b2ca8f16..c08e22cb 100644 --- a/lib/types/urlencoded.js +++ b/lib/types/urlencoded.js @@ -16,7 +16,6 @@ var bytes = require('bytes') var contentType = require('content-type') var createError = require('http-errors') var debug = require('debug')('body-parser:urlencoded') -var deprecate = require('depd')('body-parser') var read = require('../read') var typeis = require('type-is') @@ -43,12 +42,7 @@ var parsers = Object.create(null) function urlencoded (options) { var opts = options || {} - // notice because option default will flip in next major - if (opts.extended === undefined) { - deprecate('undefined extended: provide extended option') - } - - var extended = opts.extended !== false + var extended = Boolean(opts.extended) var inflate = opts.inflate !== false var limit = typeof opts.limit !== 'number' ? bytes.parse(opts.limit || '100kb') diff --git a/test/urlencoded.js b/test/urlencoded.js index e867c0f4..53fed84a 100644 --- a/test/urlencoded.js +++ b/test/urlencoded.js @@ -67,12 +67,12 @@ describe('bodyParser.urlencoded()', function () { .expect(200, '{"user":"tobi"}', done) }) - it('should parse extended syntax', function (done) { + it('should not parse extended syntax', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user[name][first]=Tobi') - .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) + .expect(200, '{"user[name][first]":"Tobi"}', done) }) describe('with extended option', function () { From 08d95a426fdbb30950ace39d1a60b85abfc100d5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 16 Sep 2014 00:34:46 -0700 Subject: [PATCH 02/21] Use on-finished to determine when body read --- HISTORY.md | 1 + lib/read.js | 3 --- lib/types/json.js | 3 ++- lib/types/raw.js | 3 ++- lib/types/text.js | 3 ++- lib/types/urlencoded.js | 3 ++- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 81928891..a198c33f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,7 @@ === * `urlencoded` parser now defaults `extended` to `false` + * Use `on-finished` to determine when body read 1.19.1 / 2021-12-10 =================== diff --git a/lib/read.js b/lib/read.js index c1026095..e4599e02 100644 --- a/lib/read.js +++ b/lib/read.js @@ -40,9 +40,6 @@ function read (req, res, next, parse, debug, options) { var opts = options var stream - // flag as parsed - req._body = true - // read options var encoding = opts.encoding !== null ? opts.encoding diff --git a/lib/types/json.js b/lib/types/json.js index 2971dc14..3436a511 100644 --- a/lib/types/json.js +++ b/lib/types/json.js @@ -16,6 +16,7 @@ var bytes = require('bytes') var contentType = require('content-type') var createError = require('http-errors') var debug = require('debug')('body-parser:json') +var isFinished = require('on-finished').isFinished var read = require('../read') var typeis = require('type-is') @@ -96,7 +97,7 @@ function json (options) { } return function jsonParser (req, res, next) { - if (req._body) { + if (isFinished(req)) { debug('body already parsed') next() return diff --git a/lib/types/raw.js b/lib/types/raw.js index f5d1b674..62063e89 100644 --- a/lib/types/raw.js +++ b/lib/types/raw.js @@ -12,6 +12,7 @@ var bytes = require('bytes') var debug = require('debug')('body-parser:raw') +var isFinished = require('on-finished').isFinished var read = require('../read') var typeis = require('type-is') @@ -53,7 +54,7 @@ function raw (options) { } return function rawParser (req, res, next) { - if (req._body) { + if (isFinished(req)) { debug('body already parsed') next() return diff --git a/lib/types/text.js b/lib/types/text.js index 083a0090..fc478f7e 100644 --- a/lib/types/text.js +++ b/lib/types/text.js @@ -13,6 +13,7 @@ var bytes = require('bytes') var contentType = require('content-type') var debug = require('debug')('body-parser:text') +var isFinished = require('on-finished').isFinished var read = require('../read') var typeis = require('type-is') @@ -55,7 +56,7 @@ function text (options) { } return function textParser (req, res, next) { - if (req._body) { + if (isFinished(req)) { debug('body already parsed') next() return diff --git a/lib/types/urlencoded.js b/lib/types/urlencoded.js index c08e22cb..468757ad 100644 --- a/lib/types/urlencoded.js +++ b/lib/types/urlencoded.js @@ -16,6 +16,7 @@ var bytes = require('bytes') var contentType = require('content-type') var createError = require('http-errors') var debug = require('debug')('body-parser:urlencoded') +var isFinished = require('on-finished').isFinished var read = require('../read') var typeis = require('type-is') @@ -71,7 +72,7 @@ function urlencoded (options) { } return function urlencodedParser (req, res, next) { - if (req._body) { + if (isFinished(req)) { debug('body already parsed') next() return From 6cbc279dc875ba1801e9ee5849f3f64e5b42f6e1 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 15 Sep 2014 18:00:43 -0700 Subject: [PATCH 03/21] Remove req.body initialization to {} --- HISTORY.md | 2 ++ README.md | 4 +++- lib/types/json.js | 4 +++- lib/types/raw.js | 4 +++- lib/types/text.js | 4 +++- lib/types/urlencoded.js | 4 +++- test/body-parser.js | 6 +++--- test/json.js | 8 ++++---- test/raw.js | 6 +++--- test/text.js | 6 +++--- test/urlencoded.js | 6 +++--- 11 files changed, 33 insertions(+), 21 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a198c33f..35f5009a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,8 @@ 2.x === + * `req.body` is no longer always initialized to `{}` + - it is left `undefined` unless a body is parsed * `urlencoded` parser now defaults `extended` to `false` * Use `on-finished` to determine when body read diff --git a/README.md b/README.md index 6da044a0..6838eef1 100644 --- a/README.md +++ b/README.md @@ -386,7 +386,7 @@ app.use(bodyParser.json()) app.use(function (req, res) { res.setHeader('Content-Type', 'text/plain') res.write('you posted:\n') - res.end(JSON.stringify(req.body, null, 2)) + res.end(String(JSON.stringify(req.body, null, 2))) }) ``` @@ -410,11 +410,13 @@ var urlencodedParser = bodyParser.urlencoded() // POST /login gets urlencoded bodies app.post('/login', urlencodedParser, function (req, res) { + if (!req.body || !req.body.username) res.sendStatus(400) res.send('welcome, ' + req.body.username) }) // POST /api/users gets JSON bodies app.post('/api/users', jsonParser, function (req, res) { + if (!req.body) res.sendStatus(400) // create user in req.body }) ``` diff --git a/lib/types/json.js b/lib/types/json.js index 3436a511..72663efa 100644 --- a/lib/types/json.js +++ b/lib/types/json.js @@ -103,7 +103,9 @@ function json (options) { return } - req.body = req.body || {} + if (!('body' in req)) { + req.body = undefined + } // skip requests without bodies if (!typeis.hasBody(req)) { diff --git a/lib/types/raw.js b/lib/types/raw.js index 62063e89..bfe274cf 100644 --- a/lib/types/raw.js +++ b/lib/types/raw.js @@ -60,7 +60,9 @@ function raw (options) { return } - req.body = req.body || {} + if (!('body' in req)) { + req.body = undefined + } // skip requests without bodies if (!typeis.hasBody(req)) { diff --git a/lib/types/text.js b/lib/types/text.js index fc478f7e..b153931b 100644 --- a/lib/types/text.js +++ b/lib/types/text.js @@ -62,7 +62,9 @@ function text (options) { return } - req.body = req.body || {} + if (!('body' in req)) { + req.body = undefined + } // skip requests without bodies if (!typeis.hasBody(req)) { diff --git a/lib/types/urlencoded.js b/lib/types/urlencoded.js index 468757ad..f4ba2cd0 100644 --- a/lib/types/urlencoded.js +++ b/lib/types/urlencoded.js @@ -78,7 +78,9 @@ function urlencoded (options) { return } - req.body = req.body || {} + if (!('body' in req)) { + req.body = undefined + } // skip requests without bodies if (!typeis.hasBody(req)) { diff --git a/test/body-parser.js b/test/body-parser.js index e32cf94d..8b3fdd83 100644 --- a/test/body-parser.js +++ b/test/body-parser.js @@ -10,10 +10,10 @@ describe('bodyParser()', function () { this.server = createServer() }) - it('should default to {}', function (done) { + it('should default req.body to undefined', function (done) { request(this.server) .post('/') - .expect(200, '{}', done) + .expect(200, 'undefined', done) }) it('should parse JSON', function (done) { @@ -149,7 +149,7 @@ function createServer (opts) { return http.createServer(function (req, res) { _bodyParser(req, res, function (err) { res.statusCode = err ? (err.status || 500) : 200 - res.end(err ? err.message : JSON.stringify(req.body)) + res.end(err ? err.message : (JSON.stringify(req.body) || typeof req.body)) }) }) } diff --git a/test/json.js b/test/json.js index ae745712..13046a82 100644 --- a/test/json.js +++ b/test/json.js @@ -36,7 +36,7 @@ describe('bodyParser.json()', function () { .get('/') .set('Content-Type', 'application/json') .unset('Transfer-Encoding') - .expect(200, '{}', done) + .expect(200, 'undefined', done) }) it('should 400 when invalid content-length', function (done) { @@ -306,7 +306,7 @@ describe('bodyParser.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') - .expect(200, '{}', done) + .expect(200, 'undefined', done) }) }) @@ -338,7 +338,7 @@ describe('bodyParser.json()', function () { .post('/') .set('Content-Type', 'application/x-json') .send('{"user":"tobi"}') - .expect(200, '{}', done) + .expect(200, 'undefined', done) }) }) @@ -653,7 +653,7 @@ function createServer (opts) { res.end(err[req.headers['x-error-property'] || 'message']) } else { res.statusCode = 200 - res.end(JSON.stringify(req.body)) + res.end(JSON.stringify(req.body) || typeof req.body) } }) }) diff --git a/test/raw.js b/test/raw.js index 36dc1ef6..31bb51b7 100644 --- a/test/raw.js +++ b/test/raw.js @@ -168,7 +168,7 @@ describe('bodyParser.raw()', function () { var test = request(this.server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) - test.expect(200, '{}', done) + test.expect(200, 'undefined', done) }) }) @@ -197,7 +197,7 @@ describe('bodyParser.raw()', function () { var test = request(this.server).post('/') test.set('Content-Type', 'application/x-foo') test.write(Buffer.from('000102', 'hex')) - test.expect(200, '{}', done) + test.expect(200, 'undefined', done) }) }) @@ -375,7 +375,7 @@ function createServer (opts) { return } - res.end(JSON.stringify(req.body)) + res.end(JSON.stringify(req.body) || typeof req.body) }) }) } diff --git a/test/text.js b/test/text.js index 01781d7f..bc359cad 100644 --- a/test/text.js +++ b/test/text.js @@ -189,7 +189,7 @@ describe('bodyParser.text()', function () { .post('/') .set('Content-Type', 'text/plain') .send('user is tobi') - .expect(200, '{}', done) + .expect(200, 'undefined', done) }) }) @@ -219,7 +219,7 @@ describe('bodyParser.text()', function () { .post('/') .set('Content-Type', 'text/xml') .send('tobi') - .expect(200, '{}', done) + .expect(200, 'undefined', done) }) }) @@ -433,7 +433,7 @@ function createServer (opts) { return http.createServer(function (req, res) { _bodyParser(req, res, function (err) { res.statusCode = err ? (err.status || 500) : 200 - res.end(err ? err.message : JSON.stringify(req.body)) + res.end(err ? err.message : (JSON.stringify(req.body) || typeof req.body)) }) }) } diff --git a/test/urlencoded.js b/test/urlencoded.js index 53fed84a..69ad7a1f 100644 --- a/test/urlencoded.js +++ b/test/urlencoded.js @@ -435,7 +435,7 @@ describe('bodyParser.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') - .expect(200, '{}', done) + .expect(200, 'undefined', done) }) }) @@ -467,7 +467,7 @@ describe('bodyParser.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-foo') .send('user=tobi') - .expect(200, '{}', done) + .expect(200, 'undefined', done) }) }) @@ -728,7 +728,7 @@ function createServer (opts) { res.end(err[req.headers['x-error-property'] || 'message']) } else { res.statusCode = 200 - res.end(JSON.stringify(req.body)) + res.end(JSON.stringify(req.body) || typeof req.body) } }) }) From f26cd9d124f0c68f3027dba159d12beba4379eec Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 17 Dec 2021 22:40:45 -0500 Subject: [PATCH 04/21] Drop support for Node.js 0.8 --- .github/workflows/ci.yml | 16 ---------------- package.json | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1805126..fafcbb07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,6 @@ jobs: strategy: matrix: name: - - Node.js 0.8 - Node.js 0.10 - Node.js 0.12 - io.js 1.x @@ -32,11 +31,6 @@ jobs: - Node.js 17.x include: - - name: Node.js 0.8 - node-version: "0.8" - npm-i: mocha@2.5.3 supertest@1.1.0 - npm-rm: nyc - - name: Node.js 0.10 node-version: "0.10" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 @@ -114,21 +108,11 @@ jobs: shell: bash -eo pipefail -l {0} run: | nvm install --default ${{ matrix.node-version }} - if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then - nvm install --alias=npm 0.10 - nvm use ${{ matrix.node-version }} - sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")" - npm config set strict-ssl false - fi dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" - name: Configure npm run: npm config set shrinkwrap false - - name: Remove npm module(s) ${{ matrix.npm-rm }} - run: npm rm --silent --save-dev ${{ matrix.npm-rm }} - if: matrix.npm-rm != '' - - name: Install npm module(s) ${{ matrix.npm-i }} run: npm install --save-dev ${{ matrix.npm-i }} if: matrix.npm-i != '' diff --git a/package.json b/package.json index 374138e8..39067b09 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "index.js" ], "engines": { - "node": ">= 0.8" + "node": ">= 0.10" }, "scripts": { "lint": "eslint .", From fbd9664f3d6f41f02a05177215b1b2263ffd94d2 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 17 Dec 2021 22:49:12 -0500 Subject: [PATCH 05/21] 2.0.0-beta.1 --- HISTORY.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 35f5009a..6c091b1b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,5 @@ -2.x -=== +2.0.0-beta.1 / 2021-12-17 +========================= * `req.body` is no longer always initialized to `{}` - it is left `undefined` unless a body is parsed diff --git a/package.json b/package.json index 39067b09..ed8fb3db 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "body-parser", "description": "Node.js body parsing middleware", - "version": "1.19.1", + "version": "2.0.0-beta.1", "contributors": [ "Douglas Christopher Wilson ", "Jonathan Ong (http://jongleberry.com)" From 3f5e93e912dbc97af8edea3c94ae6d810287735a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 18 Feb 2022 21:44:46 -0500 Subject: [PATCH 06/21] doc: fix req.body empty object note --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 6838eef1..2aa210bd 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,7 @@ var bodyParser = require('body-parser') The `bodyParser` object exposes various factories to create middlewares. All middlewares will populate the `req.body` property with the parsed body when -the `Content-Type` request header matches the `type` option, or an empty -object (`{}`) if there was no body to parse, the `Content-Type` was not matched, -or an error occurred. +the `Content-Type` request header matches the `type` option. The various errors returned by this module are described in the [errors section](#errors). From 5287a442a91308c6bad5b6e3a83daa9c70eaa2d7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 21 Feb 2023 21:11:11 -0500 Subject: [PATCH 07/21] deps: raw-body@3.0.0-beta.1 --- HISTORY.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index bf89141e..24208990 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,9 @@ This incorporates all changes after 1.19.1 up to 1.20.2. + * deps: raw-body@3.0.0-beta.1 + - deps: iconv-lite@0.5.2 + 2.0.0-beta.1 / 2021-12-17 ========================= diff --git a/package.json b/package.json index 99f2b20d..ec41dc13 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.2", + "raw-body": "3.0.0-beta.1", "type-is": "~1.6.18", "unpipe": "1.0.0" }, From 28192d690d9b266f65a198ca660780921c981f98 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 21 Feb 2023 21:22:19 -0500 Subject: [PATCH 08/21] deps: iconv-lite@0.5.2 --- HISTORY.md | 4 +++- package.json | 2 +- test/json.js | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 24208990..8ab9008c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,8 +3,10 @@ This incorporates all changes after 1.19.1 up to 1.20.2. + * deps: iconv-lite@0.5.2 + - Add encoding cp720 + - Add encoding UTF-32 * deps: raw-body@3.0.0-beta.1 - - deps: iconv-lite@0.5.2 2.0.0-beta.1 / 2021-12-17 ========================= diff --git a/package.json b/package.json index ec41dc13..4ab4d26f 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", - "iconv-lite": "0.4.24", + "iconv-lite": "0.5.2", "on-finished": "2.4.1", "qs": "6.11.0", "raw-body": "3.0.0-beta.1", diff --git a/test/json.js b/test/json.js index 44262edd..d591420d 100644 --- a/test/json.js +++ b/test/json.js @@ -616,6 +616,13 @@ describe('bodyParser.json()', function () { test.expect(200, '{"name":"论"}', done) }) + it('should parse utf-32', function (done) { + var test = request(this.server).post('/') + test.set('Content-Type', 'application/json; charset=utf-32') + test.write(Buffer.from('fffe00007b000000220000006e000000610000006d00000065000000220000003a00000022000000ba8b0000220000007d000000', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + it('should parse when content-length != char length', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/json; charset=utf-8') From f8424b1d994fef9ae8313bd5aefbfbca67e51844 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 21 Feb 2023 21:39:12 -0500 Subject: [PATCH 09/21] deps: debug@3.1.0 --- HISTORY.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 8ab9008c..6f5cbf1b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,12 @@ This incorporates all changes after 1.19.1 up to 1.20.2. + * deps: debug@3.1.0 + - Add `DEBUG_HIDE_DATE` environment variable + - Change timer to per-namespace instead of global + - Change non-TTY date format + - Remove `DEBUG_FD` environment variable support + - Support 256 namespace colors * deps: iconv-lite@0.5.2 - Add encoding cp720 - Add encoding UTF-32 diff --git a/package.json b/package.json index 4ab4d26f..8a252b0c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", - "debug": "2.6.9", + "debug": "3.1.0", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", From 42de1bf19e42c6d447dae1b3e8469b04264b9722 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 21 Feb 2023 22:25:33 -0500 Subject: [PATCH 10/21] build: remove conditional code coverage --- .github/workflows/ci.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c66c39ab..dd2e145b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,19 +159,14 @@ jobs: - name: Run tests shell: bash run: | - if npm -ps ls nyc | grep -q nyc; then - npm run test-ci - cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov" - else - npm test - fi + npm run test-ci + cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov" - name: Lint code if: steps.list_env.outputs.eslint != '' run: npm run lint - name: Collect code coverage - if: steps.list_env.outputs.nyc != '' run: | if [[ -d ./coverage ]]; then mv ./coverage "./${{ matrix.name }}" @@ -181,7 +176,6 @@ jobs: - name: Upload code coverage uses: actions/upload-artifact@v3 - if: steps.list_env.outputs.nyc != '' with: name: coverage path: ./coverage From 4d7b8211aaaf3a068759b3eea1581d873bf347d1 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 22 Feb 2023 00:14:14 -0500 Subject: [PATCH 11/21] Remove deprecated bodyParser() combination middleware --- HISTORY.md | 1 + index.js | 32 +-------- package.json | 1 - test/body-parser.js | 154 +------------------------------------------- 4 files changed, 7 insertions(+), 181 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6f5cbf1b..ca596c6f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,7 @@ This incorporates all changes after 1.19.1 up to 1.20.2. + * Remove deprecated `bodyParser()` combination middleware * deps: debug@3.1.0 - Add `DEBUG_HIDE_DATE` environment variable - Change timer to per-namespace instead of global diff --git a/index.js b/index.js index bb24d739..81fb9046 100644 --- a/index.js +++ b/index.js @@ -6,13 +6,6 @@ 'use strict' -/** - * Module dependencies. - * @private - */ - -var deprecate = require('depd')('body-parser') - /** * Cache of loaded parsers. * @private @@ -34,8 +27,7 @@ var parsers = Object.create(null) * @type {Parsers} */ -exports = module.exports = deprecate.function(bodyParser, - 'bodyParser: use individual json/urlencoded middlewares') +exports = module.exports = bodyParser /** * JSON parser. @@ -90,26 +82,8 @@ Object.defineProperty(exports, 'urlencoded', { * @public */ -function bodyParser (options) { - // use default type for parsers - var opts = Object.create(options || null, { - type: { - configurable: true, - enumerable: true, - value: undefined, - writable: true - } - }) - - var _urlencoded = exports.urlencoded(opts) - var _json = exports.json(opts) - - return function bodyParser (req, res, next) { - _json(req, res, function (err) { - if (err) return next(err) - _urlencoded(req, res, next) - }) - } +function bodyParser () { + throw new Error('The bodyParser() generic has been split into individual middleware to use instead.') } /** diff --git a/package.json b/package.json index 8a252b0c..ed977c2c 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "3.1.0", - "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.5.2", diff --git a/test/body-parser.js b/test/body-parser.js index 2fbd8ed0..ec3f6fb9 100644 --- a/test/body-parser.js +++ b/test/body-parser.js @@ -1,158 +1,10 @@ -var http = require('http') -var methods = require('methods') -var request = require('supertest') +var assert = require('assert') var bodyParser = require('..') describe('bodyParser()', function () { - before(function () { - this.server = createServer() - }) - - it('should default req.body to undefined', function (done) { - request(this.server) - .post('/') - .expect(200, 'undefined', done) - }) - - it('should parse JSON', function (done) { - request(this.server) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should parse x-www-form-urlencoded', function (done) { - request(this.server) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should handle duplicated middleware', function (done) { - var _bodyParser = bodyParser() - var server = http.createServer(function (req, res) { - _bodyParser(req, res, function (err0) { - _bodyParser(req, res, function (err1) { - var err = err0 || err1 - res.statusCode = err ? (err.status || 500) : 200 - res.end(err ? err.message : JSON.stringify(req.body)) - }) - }) - }) - - request(server) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - describe('http methods', function () { - before(function () { - var _bodyParser = bodyParser() - - this.server = http.createServer(function (req, res) { - _bodyParser(req, res, function (err) { - if (err) { - res.statusCode = 500 - res.end(err.message) - return - } - - res.statusCode = req.headers['x-expect-method'] === req.method - ? req.body.user === 'tobi' - ? 201 - : 400 - : 405 - res.end() - }) - }) - }) - - methods.slice().sort().forEach(function (method) { - if (method === 'connect') { - // except CONNECT - return - } - - it('should support ' + method.toUpperCase() + ' requests', function (done) { - request(this.server)[method]('/') - .set('Content-Type', 'application/json') - .set('Content-Length', '15') - .set('X-Expect-Method', method.toUpperCase()) - .send('{"user":"tobi"}') - .expect(201, done) - }) - }) - }) - - describe('with type option', function () { - before(function () { - this.server = createServer({ limit: '1mb', type: 'application/octet-stream' }) - }) - - it('should parse JSON', function (done) { - request(this.server) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should parse x-www-form-urlencoded', function (done) { - request(this.server) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - }) - - describe('with verify option', function () { - it('should apply to json', function (done) { - var server = createServer({ - verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } - }) - - request(server) - .post('/') - .set('Content-Type', 'application/json') - .send(' {"user":"tobi"}') - .expect(403, '[entity.verify.failed] no leading space', done) - }) - - it('should apply to urlencoded', function (done) { - var server = createServer({ - verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } - }) - - request(server) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(' user=tobi') - .expect(403, '[entity.verify.failed] no leading space', done) - }) + it('should throw an error', function () { + assert.throws(bodyParser, /bodyParser\(\) generic has been split/) }) }) - -function createServer (opts) { - var _bodyParser = bodyParser(opts) - - return http.createServer(function (req, res) { - _bodyParser(req, res, function (err) { - res.statusCode = err ? (err.status || 500) : 200 - res.end(err - ? ('[' + err.type + '] ' + err.message) - : (JSON.stringify(req.body) || typeof req.body) - ) - }) - }) -} From 87e60af4cb11a699443e1511930a2714c67c4bc2 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 22 Feb 2023 18:40:33 -0500 Subject: [PATCH 12/21] tests: enable strict mode --- test/body-parser.js | 1 + test/json.js | 1 + test/raw.js | 1 + test/text.js | 1 + test/urlencoded.js | 1 + 5 files changed, 5 insertions(+) diff --git a/test/body-parser.js b/test/body-parser.js index ec3f6fb9..0297471c 100644 --- a/test/body-parser.js +++ b/test/body-parser.js @@ -1,3 +1,4 @@ +'use strict' var assert = require('assert') diff --git a/test/json.js b/test/json.js index d591420d..1040d56b 100644 --- a/test/json.js +++ b/test/json.js @@ -1,3 +1,4 @@ +'use strict' var assert = require('assert') var asyncHooks = tryRequire('async_hooks') diff --git a/test/raw.js b/test/raw.js index 34acc537..fd2f3326 100644 --- a/test/raw.js +++ b/test/raw.js @@ -1,3 +1,4 @@ +'use strict' var assert = require('assert') var asyncHooks = tryRequire('async_hooks') diff --git a/test/text.js b/test/text.js index 2d75f06b..ec35d9b4 100644 --- a/test/text.js +++ b/test/text.js @@ -1,3 +1,4 @@ +'use strict' var assert = require('assert') var asyncHooks = tryRequire('async_hooks') diff --git a/test/urlencoded.js b/test/urlencoded.js index 4e16aa8f..8be8a5a0 100644 --- a/test/urlencoded.js +++ b/test/urlencoded.js @@ -1,3 +1,4 @@ +'use strict' var assert = require('assert') var asyncHooks = tryRequire('async_hooks') From b53363c79edc01ebaf14f4e9f1dace62361b7d96 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 23 Feb 2023 16:42:32 -0500 Subject: [PATCH 13/21] docs: add missing history entry --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index ca596c6f..a85a55ae 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -18,6 +18,7 @@ This incorporates all changes after 1.19.1 up to 1.20.2. 2.0.0-beta.1 / 2021-12-17 ========================= + * Drop support for Node.js 0.8 * `req.body` is no longer always initialized to `{}` - it is left `undefined` unless a body is parsed * `urlencoded` parser now defaults `extended` to `false` From fccaf4879e960d5e8b105759d39d4fc5ecf2f4e4 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 23 Feb 2023 16:57:45 -0500 Subject: [PATCH 14/21] 2.0.0-beta.2 --- HISTORY.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a85a55ae..ea0e0dae 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,5 @@ -2.x -=== +2.0.0-beta.2 / 2023-02-23 +========================= This incorporates all changes after 1.19.1 up to 1.20.2. diff --git a/package.json b/package.json index ed977c2c..5201dea5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "body-parser", "description": "Node.js body parsing middleware", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "contributors": [ "Douglas Christopher Wilson ", "Jonathan Ong (http://jongleberry.com)" From ddf9b7546c54c6fd39f974083f13e515e79bf094 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 22 Jul 2024 15:12:15 -0700 Subject: [PATCH 15/21] feat!: remove node less than 18 from ci --- .github/workflows/ci.yml | 134 ++++++--------------------------------- 1 file changed, 19 insertions(+), 115 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd2e145b..88c36557 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,15 @@ name: ci on: -- pull_request -- push + push: + branches: + - master + - '2.x' + paths-ignore: + - '*.md' + pull_request: + paths-ignore: + - '*.md' jobs: test: @@ -10,109 +17,22 @@ jobs: strategy: matrix: name: - - Node.js 0.10 - - Node.js 0.12 - - io.js 1.x - - io.js 2.x - - io.js 3.x - - Node.js 4.x - - Node.js 5.x - - Node.js 6.x - - Node.js 7.x - - Node.js 8.x - - Node.js 9.x - - Node.js 10.x - - Node.js 11.x - - Node.js 12.x - - Node.js 13.x - - Node.js 14.x - - Node.js 15.x - - Node.js 16.x - - Node.js 17.x - Node.js 18.x - - Node.js 19.x + - Node.js 20.x + - Node.js 22.x include: - - name: Node.js 0.10 - node-version: "0.10" - npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: Node.js 0.12 - node-version: "0.12" - npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: io.js 1.x - node-version: "1.8" - npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: io.js 2.x - node-version: "2.5" - npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: io.js 3.x - node-version: "3.3" - npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: Node.js 4.x - node-version: "4.9" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 5.x - node-version: "5.12" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 6.x - node-version: "6.17" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - - - name: Node.js 7.x - node-version: "7.10" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - - - name: Node.js 8.x - node-version: "8.17" - npm-i: mocha@7.2.0 - - - name: Node.js 9.x - node-version: "9.11" - npm-i: mocha@7.2.0 - - - name: Node.js 10.x - node-version: "10.24" - npm-i: mocha@8.4.0 - - - name: Node.js 11.x - node-version: "11.15" - npm-i: mocha@8.4.0 - - - name: Node.js 12.x - node-version: "12.22" - npm-i: mocha@9.2.2 - - - name: Node.js 13.x - node-version: "13.14" - npm-i: mocha@9.2.2 - - - name: Node.js 14.x - node-version: "14.21" - - - name: Node.js 15.x - node-version: "15.14" - - - name: Node.js 16.x - node-version: "16.19" - - - name: Node.js 17.x - node-version: "17.9" - - name: Node.js 18.x - node-version: "18.14" + node-version: "18" - - name: Node.js 19.x - node-version: "19.7" + - name: Node.js 20.x + node-version: "20" + + - name: Node.js 22.x + node-version: "22" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Node.js ${{ matrix.node-version }} shell: bash -eo pipefail -l {0} @@ -128,22 +48,6 @@ jobs: npm config set shrinkwrap false fi - - name: Install npm module(s) ${{ matrix.npm-i }} - run: npm install --save-dev ${{ matrix.npm-i }} - if: matrix.npm-i != '' - - - name: Setup Node.js version-specific dependencies - shell: bash - run: | - # eslint for linting - # - remove on Node.js < 12 - if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then - node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ - grep -E '^eslint(-|$)' | \ - sort -r | \ - xargs -n1 npm rm --silent --save-dev - fi - - name: Install Node.js dependencies run: npm install @@ -185,7 +89,7 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install lcov shell: bash From 7eb00cdc9b3c3a73ce51aefba899afab67bc8abc Mon Sep 17 00:00:00 2001 From: Andreas Lind Date: Thu, 25 Jul 2024 01:38:43 +0200 Subject: [PATCH 16/21] Also use the qs module for the simple parser (#387) * Always use the qs module, even in simple mode https://github.com/expressjs/body-parser/pull/326#issuecomment-521823039 * Create the simple and extended parser with the same function, reducing duplication * Don't mention the querystring module in the README * Fix lint --- README.md | 10 ++--- lib/types/urlencoded.js | 91 +++++------------------------------------ 2 files changed, 14 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index c9cbf023..c67f27e9 100644 --- a/README.md +++ b/README.md @@ -228,12 +228,10 @@ any of the following keys: ##### extended -The `extended` option allows to choose between parsing the URL-encoded data -with the `querystring` library (when `false`) or the `qs` library (when -`true`). The "extended" syntax allows for rich objects and arrays to be -encoded into the URL-encoded format, allowing for a JSON-like experience -with URL-encoded. For more information, please -[see the qs library](https://www.npmjs.org/package/qs#readme). +The "extended" syntax allows for rich objects and arrays to be encoded into the +URL-encoded format, allowing for a JSON-like experience with URL-encoded. For +more information, please [see the qs +library](https://www.npmjs.org/package/qs#readme). Defaults to `false`. diff --git a/lib/types/urlencoded.js b/lib/types/urlencoded.js index f4ba2cd0..6bef1141 100644 --- a/lib/types/urlencoded.js +++ b/lib/types/urlencoded.js @@ -19,6 +19,7 @@ var debug = require('debug')('body-parser:urlencoded') var isFinished = require('on-finished').isFinished var read = require('../read') var typeis = require('type-is') +var qs = require('qs') /** * Module exports. @@ -26,12 +27,6 @@ var typeis = require('type-is') module.exports = urlencoded -/** - * Cache of parser modules. - */ - -var parsers = Object.create(null) - /** * Create a middleware to parse urlencoded bodies. * @@ -56,9 +51,7 @@ function urlencoded (options) { } // create the appropriate query parser - var queryparse = extended - ? extendedparser(opts) - : simpleparser(opts) + var queryparse = createQueryParser(opts, extended) // create the appropriate type checking function var shouldParse = typeof type !== 'function' @@ -126,11 +119,10 @@ function urlencoded (options) { * @param {object} options */ -function extendedparser (options) { +function createQueryParser (options, extended) { var parameterLimit = options.parameterLimit !== undefined ? options.parameterLimit : 1000 - var parse = parser('qs') if (isNaN(parameterLimit) || parameterLimit < 1) { throw new TypeError('option parameterLimit must be a positive number') @@ -140,6 +132,8 @@ function extendedparser (options) { parameterLimit = parameterLimit | 0 } + var depth = extended ? Infinity : 0 + return function queryparse (body) { var paramCount = parameterCount(body, parameterLimit) @@ -150,13 +144,14 @@ function extendedparser (options) { }) } - var arrayLimit = Math.max(100, paramCount) + var arrayLimit = extended ? Math.max(100, paramCount) : 0 + + debug('parse ' + (extended ? 'extended ' : '') + 'urlencoding') - debug('parse extended urlencoding') - return parse(body, { + return qs.parse(body, { allowPrototypes: true, arrayLimit: arrayLimit, - depth: Infinity, + depth: depth, parameterLimit: parameterLimit }) } @@ -201,72 +196,6 @@ function parameterCount (body, limit) { return count } -/** - * Get parser for module name dynamically. - * - * @param {string} name - * @return {function} - * @api private - */ - -function parser (name) { - var mod = parsers[name] - - if (mod !== undefined) { - return mod.parse - } - - // this uses a switch for static require analysis - switch (name) { - case 'qs': - mod = require('qs') - break - case 'querystring': - mod = require('querystring') - break - } - - // store to prevent invoking require() - parsers[name] = mod - - return mod.parse -} - -/** - * Get the simple query parser. - * - * @param {object} options - */ - -function simpleparser (options) { - var parameterLimit = options.parameterLimit !== undefined - ? options.parameterLimit - : 1000 - var parse = parser('querystring') - - if (isNaN(parameterLimit) || parameterLimit < 1) { - throw new TypeError('option parameterLimit must be a positive number') - } - - if (isFinite(parameterLimit)) { - parameterLimit = parameterLimit | 0 - } - - return function queryparse (body) { - var paramCount = parameterCount(body, parameterLimit) - - if (paramCount === undefined) { - debug('too many parameters') - throw createError(413, 'too many parameters', { - type: 'parameters.too.many' - }) - } - - debug('parse urlencoding') - return parse(body, undefined, undefined, { maxKeys: parameterLimit }) - } -} - /** * Get the simple type checker. * From 35b50b5562020225f3c40471cfdbd34d760be1a8 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Thu, 25 Jul 2024 15:48:08 -0700 Subject: [PATCH 17/21] fix(deps): raw-body@^3.0.0 (#529) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5201dea5..91615585 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "iconv-lite": "0.5.2", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "3.0.0-beta.1", + "raw-body": "^3.0.0", "type-is": "~1.6.18", "unpipe": "1.0.0" }, From 6cea6bd3e1f82b23c7c090aa7d19941c638c7636 Mon Sep 17 00:00:00 2001 From: Andreas Lind Date: Tue, 30 Jul 2024 23:42:34 +0200 Subject: [PATCH 18/21] urlencoded: Support iso-8859-1, utf8 sentinel, and numeric entities (#326) * urlencoded, in extended mode: Support iso-8859-1 encoded requests, and also accept iso-8859-1 as a default encoding. * urlencoded: Support an utf8 sentinel to detect the charset. * Pass the interpretNumericEntities option through to qs. * Fix lint * Support charsets, sentinels etc. via custom decoders Works in both extended and simple mode. * Simplify * Fix empty parameter issue with utf8Sentinel in simple mode * Run all the charset/sentinel tests in both extended and simple modes * utf8Sentinel => charsetSentinel https://github.com/ljharb/qs/pull/268#discussion_r207115487 * Update qs to 6.9.1 * Always use the qs module, even in simple mode https://github.com/expressjs/body-parser/pull/326#issuecomment-521823039 * Create the simple and extended parser with the same function, reducing duplication * Don't mention the querystring module in the README * Fix lint * Update qs to 6.9.4 * Consistently call it "utf8 sentinel" * Simplify by relying on the qs module's support for detecting the charset https://github.com/expressjs/body-parser/pull/326#discussion_r1693684912 * Simplify further * Put back debug option * Document that defaultCharset defaults to utf-8 https://github.com/expressjs/body-parser/pull/326#discussion_r1693684214 --- README.md | 17 +++++++++++ lib/read.js | 2 +- lib/types/urlencoded.js | 28 ++++++++++++----- test/urlencoded.js | 68 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c67f27e9..219d63c9 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,23 @@ The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)` where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. +##### defaultCharset + +The default charset to parse as, if not specified in content-type. Must be +either `utf-8` or `iso-8859-1`. Defaults to `utf-8`. + +##### charsetSentinel + +Whether to let the value of the `utf8` parameter take precedence as the charset +selector. It requires the form to contain a parameter named `utf8` with a value +of `✓`. Defaults to `false`. + +##### interpretNumericEntities + +Whether to decode numeric entities such as `☺` when parsing an iso-8859-1 +form. Defaults to `false`. + + ## Errors The middlewares provided by this module create errors using the diff --git a/lib/read.js b/lib/read.js index 35688f99..20a18ac8 100644 --- a/lib/read.js +++ b/lib/read.js @@ -122,7 +122,7 @@ function read (req, res, next, parse, debug, options) { str = typeof body !== 'string' && encoding !== null ? iconv.decode(body, encoding) : body - req.body = parse(str) + req.body = parse(str, encoding) } catch (err) { next(createError(400, err, { body: str, diff --git a/lib/types/urlencoded.js b/lib/types/urlencoded.js index 6bef1141..1c1ff71d 100644 --- a/lib/types/urlencoded.js +++ b/lib/types/urlencoded.js @@ -45,11 +45,18 @@ function urlencoded (options) { : opts.limit var type = opts.type || 'application/x-www-form-urlencoded' var verify = opts.verify || false + var charsetSentinel = opts.charsetSentinel + var interpretNumericEntities = opts.interpretNumericEntities if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') } + var defaultCharset = opts.defaultCharset || 'utf-8' + if (defaultCharset !== 'utf-8' && defaultCharset !== 'iso-8859-1') { + throw new TypeError('option defaultCharset must be either utf-8 or iso-8859-1') + } + // create the appropriate query parser var queryparse = createQueryParser(opts, extended) @@ -58,9 +65,9 @@ function urlencoded (options) { ? typeChecker(type) : type - function parse (body) { + function parse (body, encoding) { return body.length - ? queryparse(body) + ? queryparse(body, encoding) : {} } @@ -92,8 +99,8 @@ function urlencoded (options) { } // assert charset - var charset = getCharset(req) || 'utf-8' - if (charset !== 'utf-8') { + var charset = getCharset(req) || defaultCharset + if (charset !== 'utf-8' && charset !== 'iso-8859-1') { debug('invalid charset') next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', { charset: charset, @@ -108,7 +115,9 @@ function urlencoded (options) { encoding: charset, inflate: inflate, limit: limit, - verify: verify + verify: verify, + charsetSentinel: charsetSentinel, + interpretNumericEntities: interpretNumericEntities }) } } @@ -123,6 +132,8 @@ function createQueryParser (options, extended) { var parameterLimit = options.parameterLimit !== undefined ? options.parameterLimit : 1000 + var charsetSentinel = options.charsetSentinel + var interpretNumericEntities = options.interpretNumericEntities if (isNaN(parameterLimit) || parameterLimit < 1) { throw new TypeError('option parameterLimit must be a positive number') @@ -134,7 +145,7 @@ function createQueryParser (options, extended) { var depth = extended ? Infinity : 0 - return function queryparse (body) { + return function queryparse (body, encoding) { var paramCount = parameterCount(body, parameterLimit) if (paramCount === undefined) { @@ -152,7 +163,10 @@ function createQueryParser (options, extended) { allowPrototypes: true, arrayLimit: arrayLimit, depth: depth, - parameterLimit: parameterLimit + parameterLimit: parameterLimit, + charsetSentinel: charsetSentinel, + interpretNumericEntities: interpretNumericEntities, + charset: encoding }) } } diff --git a/test/urlencoded.js b/test/urlencoded.js index 8be8a5a0..3258d094 100644 --- a/test/urlencoded.js +++ b/test/urlencoded.js @@ -48,6 +48,74 @@ describe('bodyParser.urlencoded()', function () { .expect(200, '{}', done) }) + var extendedValues = [true, false] + extendedValues.forEach(function (extended) { + describe('in ' + (extended ? 'extended' : 'simple') + ' mode', function () { + it('should parse x-www-form-urlencoded with an explicit iso-8859-1 encoding', function (done) { + var server = createServer({ extended: extended }) + request(server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded; charset=iso-8859-1') + .send('%A2=%BD') + .expect(200, '{"¢":"½"}', done) + }) + + it('should parse x-www-form-urlencoded with unspecified iso-8859-1 encoding when the defaultCharset is set to iso-8859-1', function (done) { + var server = createServer({ defaultCharset: 'iso-8859-1', extended: extended }) + request(server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('%A2=%BD') + .expect(200, '{"¢":"½"}', done) + }) + + it('should parse x-www-form-urlencoded with an unspecified iso-8859-1 encoding when the utf8 sentinel has a value of %26%2310003%3B', function (done) { + var server = createServer({ charsetSentinel: true, extended: extended }) + request(server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('utf8=%26%2310003%3B&user=%C3%B8') + .expect(200, '{"user":"ø"}', done) + }) + + it('should parse x-www-form-urlencoded with an unspecified utf-8 encoding when the utf8 sentinel has a value of %E2%9C%93 and the defaultCharset is iso-8859-1', function (done) { + var server = createServer({ charsetSentinel: true, extended: extended }) + request(server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('utf8=%E2%9C%93&user=%C3%B8') + .expect(200, '{"user":"ø"}', done) + }) + + it('should not leave an empty string parameter when removing the utf8 sentinel from the start of the string', function (done) { + var server = createServer({ charsetSentinel: true, extended: extended }) + request(server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('utf8=%E2%9C%93&foo=bar') + .expect(200, '{"foo":"bar"}', done) + }) + + it('should not leave an empty string parameter when removing the utf8 sentinel from the middle of the string', function (done) { + var server = createServer({ charsetSentinel: true, extended: extended }) + request(server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('foo=bar&utf8=%E2%9C%93&baz=quux') + .expect(200, '{"foo":"bar","baz":"quux"}', done) + }) + + it('should not leave an empty string parameter when removing the utf8 sentinel from the end of the string', function (done) { + var server = createServer({ charsetSentinel: true, extended: extended }) + request(server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('foo=bar&baz=quux&utf8=%E2%9C%93') + .expect(200, '{"foo":"bar","baz":"quux"}', done) + }) + }) + }) + it('should handle empty message-body', function (done) { request(createServer({ limit: '1kb' })) .post('/') From 07ce14d6c1708cae18a25be0c44f4eb5a6b063e3 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 17 Aug 2024 21:42:21 +0300 Subject: [PATCH 19/21] Added support for brotli ('br') content-encoding (#406) --- HISTORY.md | 5 +++++ README.md | 12 +++++++----- lib/read.js | 25 ++++++++++++++++++++----- test/json.js | 20 ++++++++++++++++++++ test/raw.js | 20 ++++++++++++++++++++ test/text.js | 20 ++++++++++++++++++++ test/urlencoded.js | 20 ++++++++++++++++++++ 7 files changed, 112 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index ea0e0dae..1aad1773 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +unreleased +========================= + +* add brotli support #406 + 2.0.0-beta.2 / 2023-02-23 ========================= diff --git a/README.md b/README.md index 219d63c9..740f4e2c 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,8 @@ The various errors returned by this module are described in the Returns middleware that only parses `json` and only looks at requests where the `Content-Type` header matches the `type` option. This parser accepts any -Unicode encoding of the body and supports automatic inflation of `gzip` and -`deflate` encodings. +Unicode encoding of the body and supports automatic inflation of `gzip`, +`br` (brotli) and `deflate` encodings. A new `body` object containing the parsed data is populated on the `request` object after the middleware (i.e. `req.body`). @@ -119,7 +119,8 @@ encoding of the request. The parsing can be aborted by throwing an error. Returns middleware that parses all bodies as a `Buffer` and only looks at requests where the `Content-Type` header matches the `type` option. This -parser supports automatic inflation of `gzip` and `deflate` encodings. +parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate` +encodings. A new `body` object containing the parsed data is populated on the `request` object after the middleware (i.e. `req.body`). This will be a `Buffer` object @@ -164,7 +165,8 @@ encoding of the request. The parsing can be aborted by throwing an error. Returns middleware that parses all bodies as a string and only looks at requests where the `Content-Type` header matches the `type` option. This -parser supports automatic inflation of `gzip` and `deflate` encodings. +parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate` +encodings. A new `body` string containing the parsed data is populated on the `request` object after the middleware (i.e. `req.body`). This will be a string of the @@ -214,7 +216,7 @@ encoding of the request. The parsing can be aborted by throwing an error. Returns middleware that only parses `urlencoded` bodies and only looks at requests where the `Content-Type` header matches the `type` option. This parser accepts only UTF-8 encoding of the body and supports automatic -inflation of `gzip` and `deflate` encodings. +inflation of `gzip`, `br` (brotli) and `deflate` encodings. A new `body` object containing the parsed data is populated on the `request` object after the middleware (i.e. `req.body`). This object will contain diff --git a/lib/read.js b/lib/read.js index 20a18ac8..a4bd446d 100644 --- a/lib/read.js +++ b/lib/read.js @@ -25,6 +25,12 @@ var zlib = require('zlib') module.exports = read +/** + * @const + * whether current node version has brotli support + */ +var hasBrotliSupport = 'createBrotliDecompress' in zlib + /** * Read a request into a buffer and parse. * @@ -174,11 +180,20 @@ function contentstream (req, debug, inflate) { stream = req stream.length = length break - default: - throw createError(415, 'unsupported content encoding "' + encoding + '"', { - encoding: encoding, - type: 'encoding.unsupported' - }) + case 'br': + if (hasBrotliSupport) { + stream = zlib.createBrotliDecompress() + debug('brotli decompress body') + req.pipe(stream) + } + break + } + + if (stream === undefined) { + throw createError(415, 'unsupported content encoding "' + encoding + '"', { + encoding: encoding, + type: 'encoding.unsupported' + }) } return stream diff --git a/test/json.js b/test/json.js index 1040d56b..3dcfd9c0 100644 --- a/test/json.js +++ b/test/json.js @@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' ? describe : describe.skip +var hasBrotliSupport = 'createBrotliDecompress' in require('zlib') +var brotlit = hasBrotliSupport ? it : it.skip +var nobrotlit = !hasBrotliSupport ? it : it.skip + describe('bodyParser.json()', function () { it('should parse JSON', function (done) { request(createServer()) @@ -683,6 +687,22 @@ describe('bodyParser.json()', function () { test.expect(200, '{"name":"论"}', done) }) + brotlit('should support brotli encoding', function (done) { + var test = request(this.server).post('/') + test.set('Content-Encoding', 'br') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('8b06807b226e616d65223a22e8aeba227d03', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + nobrotlit('should throw 415 if there\'s no brotli support', function (done) { + var test = request(this.server).post('/') + test.set('Content-Encoding', 'br') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('8b06807b226e616d65223a22e8aeba227d03', 'hex')) + test.expect(415, 'unsupported content encoding "br"', done) + }) + it('should be case-insensitive', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'GZIP') diff --git a/test/raw.js b/test/raw.js index fd2f3326..bd0b564c 100644 --- a/test/raw.js +++ b/test/raw.js @@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' ? describe : describe.skip +var hasBrotliSupport = 'createBrotliDecompress' in require('zlib') +var brotlit = hasBrotliSupport ? it : it.skip +var nobrotlit = !hasBrotliSupport ? it : it.skip + describe('bodyParser.raw()', function () { before(function () { this.server = createServer() @@ -455,6 +459,22 @@ describe('bodyParser.raw()', function () { test.expect(200, 'buf:6e616d653de8aeba', done) }) + brotlit('should support brotli encoding', function (done) { + var test = request(this.server).post('/') + test.set('Content-Encoding', 'br') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex')) + test.expect(200, 'buf:6e616d653de8aeba', done) + }) + + nobrotlit('should throw 415 if there\'s no brotli support', function (done) { + var test = request(this.server).post('/') + test.set('Content-Encoding', 'br') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex')) + test.expect(415, 'unsupported content encoding "br"', done) + }) + it('should be case-insensitive', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'GZIP') diff --git a/test/text.js b/test/text.js index ec35d9b4..4eae37ac 100644 --- a/test/text.js +++ b/test/text.js @@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' ? describe : describe.skip +var hasBrotliSupport = 'createBrotliDecompress' in require('zlib') +var brotlit = hasBrotliSupport ? it : it.skip +var nobrotlit = !hasBrotliSupport ? it : it.skip + describe('bodyParser.text()', function () { before(function () { this.server = createServer() @@ -525,6 +529,22 @@ describe('bodyParser.text()', function () { test.expect(200, '"name is 论"', done) }) + brotlit('should support brotli encoding', function (done) { + var test = request(this.server).post('/') + test.set('Content-Encoding', 'br') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('0b05806e616d6520697320e8aeba03', 'hex')) + test.expect(200, '"name is 论"', done) + }) + + nobrotlit('should throw 415 if there\'s no brotli support', function (done) { + var test = request(this.server).post('/') + test.set('Content-Encoding', 'br') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('0b05806e616d6520697320e8aeba03', 'hex')) + test.expect(415, 'unsupported content encoding "br"', done) + }) + it('should be case-insensitive', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'GZIP') diff --git a/test/urlencoded.js b/test/urlencoded.js index 3258d094..5ce2ffc5 100644 --- a/test/urlencoded.js +++ b/test/urlencoded.js @@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' ? describe : describe.skip +var hasBrotliSupport = 'createBrotliDecompress' in require('zlib') +var brotlit = hasBrotliSupport ? it : it.skip +var nobrotlit = !hasBrotliSupport ? it : it.skip + describe('bodyParser.urlencoded()', function () { before(function () { this.server = createServer() @@ -831,6 +835,22 @@ describe('bodyParser.urlencoded()', function () { test.expect(200, '{"name":"论"}', done) }) + brotlit('should support brotli encoding', function (done) { + var test = request(this.server).post('/') + test.set('Content-Encoding', 'br') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + nobrotlit('should throw 415 if there\'s no brotli support', function (done) { + var test = request(this.server).post('/') + test.set('Content-Encoding', 'br') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex')) + test.expect(415, 'unsupported content encoding "br"', done) + }) + it('should be case-insensitive', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'GZIP') From afd0f39944ddd1486c9a55c1a3c8251f2b0cdf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Tue, 10 Sep 2024 04:53:08 +0200 Subject: [PATCH 20/21] feat: add option to customize the depth with a default value of 32 - Update documentation to reflect the new features and errors - Update the changelog - Upgrade to `qs@6.13.0` - Add the `depth` option to define the depth of parsing while parsing the query string - Enable the `strictDepth` option by default in `qs.parse` - Add a 400 status code when the depth of the query string exceeds the limit defined by the `depth` option - Reduce the default depth limit to 32 --- HISTORY.md | 9 ++++- README.md | 8 ++++ lib/types/urlencoded.js | 39 ++++++++++++++++++-- package.json | 2 +- test/urlencoded.js | 82 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 130 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 1aad1773..478609fa 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,6 @@ unreleased ========================= - +* Propagate changes from 1.20.3 * add brotli support #406 2.0.0-beta.2 / 2023-02-23 @@ -29,6 +29,13 @@ This incorporates all changes after 1.19.1 up to 1.20.2. * `urlencoded` parser now defaults `extended` to `false` * Use `on-finished` to determine when body read +1.20.3 / 2024-09-10 +=================== + + * deps: qs@6.13.0 + * add `depth` option to customize the depth level in the parser + * IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`) + 1.20.2 / 2023-02-21 =================== diff --git a/README.md b/README.md index 740f4e2c..e37ef8d1 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,10 @@ Whether to decode numeric entities such as `☺` when parsing an iso-8859-1 form. Defaults to `false`. +#### depth + +The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible. + ## Errors The middlewares provided by this module create errors using the @@ -386,6 +390,10 @@ as well as in the `encoding` property. The `status` property is set to `415`, the `type` property is set to `'encoding.unsupported'`, and the `encoding` property is set to the encoding that is unsupported. +### The input exceeded the depth + +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. + ## Examples ### Express/Connect top-level generic diff --git a/lib/types/urlencoded.js b/lib/types/urlencoded.js index 1c1ff71d..bfd44c91 100644 --- a/lib/types/urlencoded.js +++ b/lib/types/urlencoded.js @@ -52,6 +52,10 @@ function urlencoded (options) { throw new TypeError('option verify must be function') } + var depth = typeof opts.depth !== 'number' + ? Number(opts.depth || 32) + : opts.depth + var defaultCharset = opts.defaultCharset || 'utf-8' if (defaultCharset !== 'utf-8' && defaultCharset !== 'iso-8859-1') { throw new TypeError('option defaultCharset must be either utf-8 or iso-8859-1') @@ -117,7 +121,8 @@ function urlencoded (options) { limit: limit, verify: verify, charsetSentinel: charsetSentinel, - interpretNumericEntities: interpretNumericEntities + interpretNumericEntities: interpretNumericEntities, + depth: depth }) } } @@ -135,16 +140,22 @@ function createQueryParser (options, extended) { var charsetSentinel = options.charsetSentinel var interpretNumericEntities = options.interpretNumericEntities + var depth = typeof options.depth !== 'number' + ? Number(options.depth || 32) + : options.depth + if (isNaN(parameterLimit) || parameterLimit < 1) { throw new TypeError('option parameterLimit must be a positive number') } + if (isNaN(depth) || depth < 0) { + throw new TypeError('option depth must be a zero or a positive number') + } + if (isFinite(parameterLimit)) { parameterLimit = parameterLimit | 0 } - var depth = extended ? Infinity : 0 - return function queryparse (body, encoding) { var paramCount = parameterCount(body, parameterLimit) @@ -158,6 +169,28 @@ function createQueryParser (options, extended) { var arrayLimit = extended ? Math.max(100, paramCount) : 0 debug('parse ' + (extended ? 'extended ' : '') + 'urlencoding') + try { + return qs.parse(body, { + allowPrototypes: true, + arrayLimit: arrayLimit, + depth: depth, + charsetSentinel: charsetSentinel, + interpretNumericEntities: interpretNumericEntities, + charset: encoding, + parameterLimit: parameterLimit, + strictDepth: true + }) + } catch (err) { + if (err instanceof RangeError) { + throw createError(400, 'The input exceeded the depth', { + type: 'querystring.parse.rangeError' + }) + } else { + throw err + } + } + + return qs.parse(body, { allowPrototypes: true, diff --git a/package.json b/package.json index 91615585..39b46a58 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.5.2", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "^3.0.0", "type-is": "~1.6.18", "unpipe": "1.0.0" diff --git a/test/urlencoded.js b/test/urlencoded.js index 5ce2ffc5..dbf9841f 100644 --- a/test/urlencoded.js +++ b/test/urlencoded.js @@ -55,7 +55,7 @@ describe('bodyParser.urlencoded()', function () { var extendedValues = [true, false] extendedValues.forEach(function (extended) { describe('in ' + (extended ? 'extended' : 'simple') + ' mode', function () { - it('should parse x-www-form-urlencoded with an explicit iso-8859-1 encoding', function (done) { + it.skip('should parse x-www-form-urlencoded with an explicit iso-8859-1 encoding', function (done) { var server = createServer({ extended: extended }) request(server) .post('/') @@ -166,7 +166,7 @@ describe('bodyParser.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user[name][first]=Tobi') - .expect(200, '{"user[name][first]":"Tobi"}', done) + .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) }) describe('with extended option', function () { @@ -180,7 +180,7 @@ describe('bodyParser.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user[name][first]=Tobi') - .expect(200, '{"user[name][first]":"Tobi"}', done) + .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) }) it('should parse multiple key instances', function (done) { @@ -268,7 +268,7 @@ describe('bodyParser.urlencoded()', function () { it('should parse deep object', function (done) { var str = 'foo' - for (var i = 0; i < 500; i++) { + for (var i = 0; i < 32; i++) { str += '[p]' } @@ -286,13 +286,85 @@ describe('bodyParser.urlencoded()', function () { var depth = 0 var ref = obj.foo while ((ref = ref.p)) { depth++ } - assert.strictEqual(depth, 500) + assert.strictEqual(depth, 32) }) .expect(200, done) }) }) }) + describe('with depth option', function () { + describe('when custom value set', function () { + it('should reject non possitive numbers', function () { + assert.throws(createServer.bind(null, { extended: true, depth: -1 }), + /TypeError: option depth must be a zero or a positive number/) + assert.throws(createServer.bind(null, { extended: true, depth: NaN }), + /TypeError: option depth must be a zero or a positive number/) + assert.throws(createServer.bind(null, { extended: true, depth: 'beep' }), + /TypeError: option depth must be a zero or a positive number/) + }) + + it('should parse up to the specified depth', function (done) { + this.server = createServer({ extended: true, depth: 10 }) + request(this.server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('a[b][c][d]=value') + .expect(200, '{"a":{"b":{"c":{"d":"value"}}}}', done) + }) + + it('should not parse beyond the specified depth', function (done) { + this.server = createServer({ extended: true, depth: 1 }) + request(this.server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('a[b][c][d][e]=value') + .expect(400, '[querystring.parse.rangeError] The input exceeded the depth', done) + }) + }) + + describe('when default value', function () { + before(function () { + this.server = createServer({ }) + }) + + it('should parse deeply nested objects', function (done) { + var deepObject = 'a' + for (var i = 0; i < 32; i++) { + deepObject += '[p]' + } + deepObject += '=value' + + request(this.server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(deepObject) + .expect(function (res) { + var obj = JSON.parse(res.text) + var depth = 0 + var ref = obj.a + while ((ref = ref.p)) { depth++ } + assert.strictEqual(depth, 32) + }) + .expect(200, done) + }) + + it('should not parse beyond the specified depth', function (done) { + var deepObject = 'a' + for (var i = 0; i < 33; i++) { + deepObject += '[p]' + } + deepObject += '=value' + + request(this.server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(deepObject) + .expect(400, '[querystring.parse.rangeError] The input exceeded the depth', done) + }) + }) + }) + describe('with inflate option', function () { describe('when false', function () { before(function () { From a50ab7441412daa7935e44cc854c3a9a5d4c8b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Tue, 10 Sep 2024 05:07:39 +0200 Subject: [PATCH 21/21] 2.0.0 --- HISTORY.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 478609fa..a1c98849 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,4 @@ -unreleased +2.1.0 / 2024-09-10 ========================= * Propagate changes from 1.20.3 * add brotli support #406 diff --git a/package.json b/package.json index 39b46a58..f16cc5d6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "body-parser", "description": "Node.js body parsing middleware", - "version": "2.0.0-beta.2", + "version": "2.1.0", "contributors": [ "Douglas Christopher Wilson ", "Jonathan Ong (http://jongleberry.com)"