From fde8f647d368df1dc62d55ce4d68370ef3eb0a49 Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Wed, 17 May 2017 16:00:49 +0800 Subject: [PATCH 001/479] examples: fix route in params example closes #3310 --- examples/params/index.js | 2 +- test/acceptance/params.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/params/index.js b/examples/params/index.js index d70f0beeeb1..f918b5fd30d 100644 --- a/examples/params/index.js +++ b/examples/params/index.js @@ -68,7 +68,7 @@ app.get('/users/:from-:to', function(req, res, next){ var from = req.params.from; var to = req.params.to; var names = users.map(function(user){ return user.name; }); - res.send('users ' + names.slice(from, to).join(', ')); + res.send('users ' + names.slice(from, to + 1).join(', ')); }); /* istanbul ignore next */ diff --git a/test/acceptance/params.js b/test/acceptance/params.js index 56b7a72ce0f..e7c30cf7732 100644 --- a/test/acceptance/params.js +++ b/test/acceptance/params.js @@ -29,8 +29,8 @@ describe('params', function(){ describe('GET /users/0-2', function(){ it('should respond with three users', function(done){ request(app) - .get('/users/0-2') - .expect(/users tj, tobi/,done) + .get('/users/0-2') + .expect(/users tj, tobi, loki/, done) }) }) From 60f87f8074c28a1727305530058d8c2c9596387c Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Wed, 17 May 2017 17:04:10 +0800 Subject: [PATCH 002/479] examples: fix posts link in route-separation example closes #3310 --- examples/route-separation/views/index.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/route-separation/views/index.ejs b/examples/route-separation/views/index.ejs index 6d1afb4ac3d..2a0b095fa36 100644 --- a/examples/route-separation/views/index.ejs +++ b/examples/route-separation/views/index.ejs @@ -4,7 +4,7 @@ <% include footer %> From cf37240e7306c5085f3d2a232a4649279d020667 Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Wed, 17 May 2017 17:10:38 +0800 Subject: [PATCH 003/479] examples: fix reference error in view-constructor closes #3310 --- examples/view-constructor/github-view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/view-constructor/github-view.js b/examples/view-constructor/github-view.js index 53890291056..0a98a908434 100644 --- a/examples/view-constructor/github-view.js +++ b/examples/view-constructor/github-view.js @@ -2,7 +2,7 @@ * Module dependencies. */ -var http = require('http'); +var https = require('https'); var path = require('path'); var extname = path.extname; From 9f019c8c6966736803a65eb4a96d0e7e87e85ede Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Wed, 17 May 2017 23:55:27 +0800 Subject: [PATCH 004/479] examples: add comment about Redis install in examples closes #3310 --- examples/online/index.js | 6 +++++- examples/search/index.js | 6 +++++- examples/session/index.js | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/online/index.js b/examples/online/index.js index 5cdaa6ca8d2..f14474c08d3 100644 --- a/examples/online/index.js +++ b/examples/online/index.js @@ -1,4 +1,8 @@ -// first: + +// install redis first: +// https://redis.io/ + +// then: // $ npm install redis online // $ redis-server diff --git a/examples/search/index.js b/examples/search/index.js index 79f5d9f4d36..246993caa5a 100644 --- a/examples/search/index.js +++ b/examples/search/index.js @@ -1,4 +1,8 @@ -// first: + +// install redis first: +// https://redis.io/ + +// then: // $ npm install redis // $ redis-server diff --git a/examples/session/index.js b/examples/session/index.js index de41a77d2c0..9bae48b8d33 100644 --- a/examples/session/index.js +++ b/examples/session/index.js @@ -1,4 +1,8 @@ -// first: + +// install redis first: +// https://redis.io/ + +// then: // $ npm install redis // $ redis-server From 9467a392e33ad1575f2d76aad5fc19f9290d6cd6 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 4 Jun 2017 19:09:25 -0400 Subject: [PATCH 005/479] build: Node.js@7.10 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b4beea77f36..c9172c302ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ node_js: - "4.8" - "5.12" - "6.10" - - "7.9" + - "7.10" matrix: include: - node_js: "8.0" diff --git a/appveyor.yml b/appveyor.yml index cd4f9d23e5e..9fbc9d39a38 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,7 @@ environment: - nodejs_version: "4.8" - nodejs_version: "5.12" - nodejs_version: "6.10" - - nodejs_version: "7.9" + - nodejs_version: "7.10" cache: - node_modules install: From 48777dc37774eaa2c328a1d9bb9541dfa47ca90f Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 4 Jun 2017 19:12:30 -0400 Subject: [PATCH 006/479] build: mocha@3.4.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98adaea686a..1788e9ffd16 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "istanbul": "0.4.5", "marked": "0.3.6", "method-override": "2.3.8", - "mocha": "3.4.1", + "mocha": "3.4.2", "morgan": "1.8.1", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", From deffce5704913df9e6b00aca5536345610222417 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 30 Jun 2017 23:47:12 -0400 Subject: [PATCH 007/479] deps: qs@6.5.0 --- History.md | 5 +++++ lib/middleware/query.js | 3 ++- package.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 83b439c63e8..36fadc4b990 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * deps: qs@6.5.0 + 4.15.3 / 2017-05-16 =================== diff --git a/lib/middleware/query.js b/lib/middleware/query.js index 5f76f8458f0..7e9166947af 100644 --- a/lib/middleware/query.js +++ b/lib/middleware/query.js @@ -12,6 +12,7 @@ * Module dependencies. */ +var merge = require('utils-merge') var parseUrl = require('parseurl'); var qs = require('qs'); @@ -22,7 +23,7 @@ var qs = require('qs'); */ module.exports = function query(options) { - var opts = Object.create(options || null); + var opts = merge({}, options) var queryparse = qs.parse; if (typeof options === 'function') { diff --git a/package.json b/package.json index 1788e9ffd16..cb37b98a7f5 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "parseurl": "~1.3.1", "path-to-regexp": "0.1.7", "proxy-addr": "~1.1.4", - "qs": "6.4.0", + "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.3", "serve-static": "1.12.3", From bd5951e603c16c1db779a76bc5e500c243f96cf8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 30 Jun 2017 23:51:18 -0400 Subject: [PATCH 008/479] deps: debug@2.6.8 closes #3286 closes #3337 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 36fadc4b990..59663eb712d 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * deps: debug@2.6.8 * deps: qs@6.5.0 4.15.3 / 2017-05-16 diff --git a/package.json b/package.json index cb37b98a7f5..674c8601ab0 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "content-type": "~1.0.2", "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "2.6.7", + "debug": "2.6.8", "depd": "~1.1.0", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", From 1adee79e636400c734f2307c1ef4fb0fff5db92b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 30 Jun 2017 23:58:01 -0400 Subject: [PATCH 009/479] deps: update example dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 674c8601ab0..7f401132cae 100644 --- a/package.json +++ b/package.json @@ -58,16 +58,16 @@ }, "devDependencies": { "after": "0.8.2", - "body-parser": "1.17.1", + "body-parser": "1.17.2", "cookie-parser": "~1.4.3", "ejs": "2.5.6", - "express-session": "1.15.2", + "express-session": "1.15.3", "hbs": "4.0.1", "istanbul": "0.4.5", "marked": "0.3.6", - "method-override": "2.3.8", + "method-override": "2.3.9", "mocha": "3.4.2", - "morgan": "1.8.1", + "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", "should": "11.2.1", From 04beebb2c087e3b9795d6f1c3d0bd1112bf1f244 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 26 Jul 2017 11:52:42 -0400 Subject: [PATCH 010/479] build: Node.js@6.11 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c9172c302ca..3e899dd2351 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ node_js: - "3.3" - "4.8" - "5.12" - - "6.10" + - "6.11" - "7.10" matrix: include: diff --git a/appveyor.yml b/appveyor.yml index 9fbc9d39a38..ed8d4aa9f05 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ environment: - nodejs_version: "3.3" - nodejs_version: "4.8" - nodejs_version: "5.12" - - nodejs_version: "6.10" + - nodejs_version: "6.11" - nodejs_version: "7.10" cache: - node_modules From 43dff4ceb3446397aa3fe8c48b2de67b9e76a031 Mon Sep 17 00:00:00 2001 From: Piper Chester Date: Tue, 27 Jun 2017 11:48:08 +0200 Subject: [PATCH 011/479] docs: fix GitHub capitalization closes #3353 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 786756a1cd2..17326615776 100644 --- a/Readme.md +++ b/Readme.md @@ -39,7 +39,7 @@ $ npm install express * [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)] * [#express](https://webchat.freenode.net/?channels=express) on freenode IRC - * [Github Organization](https://github.com/expressjs) for Official Middleware & Modules + * [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules * Visit the [Wiki](https://github.com/expressjs/express/wiki) * [Google Group](https://groups.google.com/group/express-js) for discussion * [Gitter](https://gitter.im/expressjs/express) for support and discussion From 5e16f400f17ca4bac48226446b09299e392db3b2 Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Fri, 19 May 2017 00:18:28 +0800 Subject: [PATCH 012/479] examples: use 1-based visitor count in cookie-sessions closes #3312 --- examples/cookie-sessions/index.js | 5 ++--- test/acceptance/cookie-sessions.js | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/cookie-sessions/index.js b/examples/cookie-sessions/index.js index 73653f63af9..1dda15de612 100644 --- a/examples/cookie-sessions/index.js +++ b/examples/cookie-sessions/index.js @@ -15,9 +15,8 @@ app.use(count); // custom middleware function count(req, res) { - req.session.count = req.session.count || 0; - var n = req.session.count++; - res.send('viewed ' + n + ' times\n'); + req.session.count = (req.session.count || 0) + 1 + res.send('viewed ' + req.session.count + ' times\n') } /* istanbul ignore next */ diff --git a/test/acceptance/cookie-sessions.js b/test/acceptance/cookie-sessions.js index 611ebe462a2..d438cfe6d5d 100644 --- a/test/acceptance/cookie-sessions.js +++ b/test/acceptance/cookie-sessions.js @@ -7,7 +7,7 @@ describe('cookie-sessions', function () { it('should display no views', function (done) { request(app) .get('/') - .expect(200, 'viewed 0 times\n', done) + .expect(200, 'viewed 1 times\n', done) }) it('should set a session cookie', function (done) { @@ -20,12 +20,12 @@ describe('cookie-sessions', function () { it('should display 1 view on revisit', function (done) { request(app) .get('/') - .expect(200, 'viewed 0 times\n', function (err, res) { + .expect(200, 'viewed 1 times\n', function (err, res) { if (err) return done(err) request(app) .get('/') .set('Cookie', getCookies(res)) - .expect(200, 'viewed 1 times\n', done) + .expect(200, 'viewed 2 times\n', done) }) }) }) From 582381bcebf2a2344e7e054eed9606cc2221dd97 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 26 Jul 2017 13:09:46 -0400 Subject: [PATCH 013/479] deps: proxy-addr@~1.1.5 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 59663eb712d..a724e84231f 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,9 @@ unreleased ========== * deps: debug@2.6.8 + * deps: proxy-addr@~1.1.5 + - Fix array argument being altered + - deps: ipaddr.js@1.4.0 * deps: qs@6.5.0 4.15.3 / 2017-05-16 diff --git a/package.json b/package.json index 7f401132cae..daf1f1feb1f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.1", "path-to-regexp": "0.1.7", - "proxy-addr": "~1.1.4", + "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.3", From 3eb16c233c5bf76fb12558101565971372693c73 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 2 Aug 2017 23:30:47 -0400 Subject: [PATCH 014/479] deps: depd@~1.1.1 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index a724e84231f..af2bcfb384c 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,8 @@ unreleased ========== * deps: debug@2.6.8 + * deps: depd@~1.1.1 + - Remove unnecessary `Buffer` loading * deps: proxy-addr@~1.1.5 - Fix array argument being altered - deps: ipaddr.js@1.4.0 diff --git a/package.json b/package.json index daf1f1feb1f..0a3848134fd 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.8", - "depd": "~1.1.0", + "depd": "~1.1.1", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", "etag": "~1.8.0", From b2af1018215540964390aab0c739c1e688865122 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 2 Aug 2017 23:32:44 -0400 Subject: [PATCH 015/479] build: ejs@2.5.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a3848134fd..c14b8126b11 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "after": "0.8.2", "body-parser": "1.17.2", "cookie-parser": "~1.4.3", - "ejs": "2.5.6", + "ejs": "2.5.7", "express-session": "1.15.3", "hbs": "4.0.1", "istanbul": "0.4.5", From daf66beda49ebac6086b81dd1896a34395306a71 Mon Sep 17 00:00:00 2001 From: Hung HOANG Date: Thu, 3 Aug 2017 11:33:29 +0200 Subject: [PATCH 016/479] examples: fix path join in ejs example fixes #3382 closes #3383 closes #3385 --- examples/ejs/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ejs/index.js b/examples/ejs/index.js index b868bdd7cb9..72780912938 100644 --- a/examples/ejs/index.js +++ b/examples/ejs/index.js @@ -26,7 +26,7 @@ app.set('views', path.join(__dirname, 'views')); // Path to our public directory -app.use(express.static(path.join(__dirname + 'public'))); +app.use(express.static(path.join(__dirname, 'public'))); // Without this you would need to // supply the extension to res.render() From 85770a71fc3f3c7f3a1efe3e01d9f0c5fd68f82e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 4 Aug 2017 00:25:59 -0400 Subject: [PATCH 017/479] deps: finalhandler@~1.0.4 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index af2bcfb384c..c67fd941745 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,8 @@ unreleased * deps: debug@2.6.8 * deps: depd@~1.1.1 - Remove unnecessary `Buffer` loading + * deps: finalhandler@~1.0.4 + - deps: debug@2.6.8 * deps: proxy-addr@~1.1.5 - Fix array argument being altered - deps: ipaddr.js@1.4.0 diff --git a/package.json b/package.json index c14b8126b11..839f370cbcb 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "encodeurl": "~1.0.1", "escape-html": "~1.0.3", "etag": "~1.8.0", - "finalhandler": "~1.0.3", + "finalhandler": "~1.0.4", "fresh": "0.5.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From e0aa8bf74eed76df4e5cf02005233d9de2401348 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 4 Aug 2017 00:26:50 -0400 Subject: [PATCH 018/479] build: mocha@3.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 839f370cbcb..15e80f1934b 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "istanbul": "0.4.5", "marked": "0.3.6", "method-override": "2.3.9", - "mocha": "3.4.2", + "mocha": "3.5.0", "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", From 713d2aed93586a7c11fc2c8beeaa0c03c6f565c9 Mon Sep 17 00:00:00 2001 From: Daniel Walasek Date: Sat, 5 Aug 2017 12:42:45 +0200 Subject: [PATCH 019/479] tests: fix incorrect should usage closes #3387 --- test/Route.js | 14 +++++++------- test/Router.js | 2 +- test/app.js | 4 ++-- test/app.param.js | 6 +++--- test/app.render.js | 2 +- test/app.routes.error.js | 8 ++++---- test/exports.js | 8 ++++---- test/req.acceptsEncoding.js | 6 +++--- test/req.acceptsEncodings.js | 6 +++--- test/req.acceptsLanguage.js | 12 ++++++------ test/req.acceptsLanguages.js | 12 ++++++------ test/req.xhr.js | 8 ++++---- test/res.sendFile.js | 14 +++++++------- 13 files changed, 51 insertions(+), 51 deletions(-) diff --git a/test/Route.js b/test/Route.js index ada54086bf0..d7a80bdbc01 100644 --- a/test/Route.js +++ b/test/Route.js @@ -25,7 +25,7 @@ describe('Route', function(){ route.dispatch(req, {}, function (err) { if (err) return done(err); - should(req.called).be.ok; + should(req.called).be.ok() done(); }); }) @@ -84,7 +84,7 @@ describe('Route', function(){ route.dispatch(req, {}, function (err) { if (err) return done(err); - should(req.called).be.ok; + should(req.called).be.ok() done(); }); }) @@ -104,7 +104,7 @@ describe('Route', function(){ route.dispatch(req, {}, function (err) { if (err) return done(err); - should(req.called).be.true; + should(req.called).be.true() done(); }); }) @@ -156,7 +156,7 @@ describe('Route', function(){ }); route.dispatch(req, {}, function (err) { - should(err).be.ok; + should(err).be.ok() should(err.message).equal('foobar'); req.order.should.equal('a'); done(); @@ -182,7 +182,7 @@ describe('Route', function(){ }); route.dispatch(req, {}, function (err) { - should(err).be.ok; + should(err).be.ok() should(err.message).equal('foobar'); req.order.should.equal('a'); done(); @@ -222,7 +222,7 @@ describe('Route', function(){ }); route.dispatch(req, {}, function(err){ - should(err).be.ok; + should(err).be.ok() err.message.should.equal('boom!'); done(); }); @@ -234,7 +234,7 @@ describe('Route', function(){ route.all(function(err, req, res, next){ // this should not execute - true.should.be.false; + true.should.be.false() }); route.dispatch(req, {}, done); diff --git a/test/Router.js b/test/Router.js index 01a6e2c472b..18153d29267 100644 --- a/test/Router.js +++ b/test/Router.js @@ -47,7 +47,7 @@ describe('Router', function(){ var router = new Router(); router.use(function (req, res) { - false.should.be.true; + false.should.be.true() }); router.handle({ url: '', method: 'GET' }, {}, done); diff --git a/test/app.js b/test/app.js index 941d35ff1cc..e52365c36bb 100644 --- a/test/app.js +++ b/test/app.js @@ -86,7 +86,7 @@ describe('in development', function(){ it('should disable "view cache"', function(){ process.env.NODE_ENV = 'development'; var app = express(); - app.enabled('view cache').should.be.false; + app.enabled('view cache').should.be.false() process.env.NODE_ENV = 'test'; }) }) @@ -95,7 +95,7 @@ describe('in production', function(){ it('should enable "view cache"', function(){ process.env.NODE_ENV = 'production'; var app = express(); - app.enabled('view cache').should.be.true; + app.enabled('view cache').should.be.true() process.env.NODE_ENV = 'test'; }) }) diff --git a/test/app.param.js b/test/app.param.js index c7a375418cd..ba43e46f8e3 100644 --- a/test/app.param.js +++ b/test/app.param.js @@ -57,13 +57,13 @@ describe('app', function(){ app.get('/post/:id', function(req, res){ var id = req.params.id; - id.should.be.a.Number; + id.should.be.a.Number() res.send('' + id); }); app.get('/user/:uid', function(req, res){ var id = req.params.id; - id.should.be.a.Number; + id.should.be.a.Number() res.send('' + id); }); @@ -91,7 +91,7 @@ describe('app', function(){ app.get('/user/:id', function(req, res){ var id = req.params.id; - id.should.be.a.Number; + id.should.be.a.Number() res.send('' + id); }); diff --git a/test/app.render.js b/test/app.render.js index 729b1c97cc8..1485098f582 100644 --- a/test/app.render.js +++ b/test/app.render.js @@ -72,7 +72,7 @@ describe('app', function(){ app.set('view', View); app.render('something', function(err, str){ - err.should.be.ok; + err.should.be.ok() err.message.should.equal('err!'); done(); }) diff --git a/test/app.routes.error.js b/test/app.routes.error.js index 7c49d50ffe2..cbbc23ef574 100644 --- a/test/app.routes.error.js +++ b/test/app.routes.error.js @@ -44,10 +44,10 @@ describe('app', function(){ d = true; next(); }, function(req, res){ - a.should.be.false; - b.should.be.true; - c.should.be.true; - d.should.be.false; + a.should.be.false() + b.should.be.true() + c.should.be.true() + d.should.be.false() res.send(204); }); diff --git a/test/exports.js b/test/exports.js index d34a7b1cf3e..2a80eedbbe8 100644 --- a/test/exports.js +++ b/test/exports.js @@ -5,19 +5,19 @@ var should = require('should'); describe('exports', function(){ it('should expose Router', function(){ - express.Router.should.be.a.Function; + express.Router.should.be.a.Function() }) it('should expose the application prototype', function(){ - express.application.set.should.be.a.Function; + express.application.set.should.be.a.Function() }) it('should expose the request prototype', function(){ - express.request.accepts.should.be.a.Function; + express.request.accepts.should.be.a.Function() }) it('should expose the response prototype', function(){ - express.response.send.should.be.a.Function; + express.response.send.should.be.a.Function() }) it('should permit modifying the .application prototype', function(){ diff --git a/test/req.acceptsEncoding.js b/test/req.acceptsEncoding.js index 12708fc0144..9ed9197829f 100644 --- a/test/req.acceptsEncoding.js +++ b/test/req.acceptsEncoding.js @@ -8,8 +8,8 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsEncoding('gzip').should.be.ok; - req.acceptsEncoding('deflate').should.be.ok; + req.acceptsEncoding('gzip').should.be.ok() + req.acceptsEncoding('deflate').should.be.ok() res.end(); }); @@ -23,7 +23,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsEncoding('bogus').should.not.be.ok; + req.acceptsEncoding('bogus').should.not.be.ok() res.end(); }); diff --git a/test/req.acceptsEncodings.js b/test/req.acceptsEncodings.js index c036c297691..aba8ea5fbeb 100644 --- a/test/req.acceptsEncodings.js +++ b/test/req.acceptsEncodings.js @@ -8,8 +8,8 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsEncodings('gzip').should.be.ok; - req.acceptsEncodings('deflate').should.be.ok; + req.acceptsEncodings('gzip').should.be.ok() + req.acceptsEncodings('deflate').should.be.ok() res.end(); }); @@ -23,7 +23,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsEncodings('bogus').should.not.be.ok; + req.acceptsEncodings('bogus').should.not.be.ok() res.end(); }); diff --git a/test/req.acceptsLanguage.js b/test/req.acceptsLanguage.js index b14d920bd69..1c7c5fd86f6 100644 --- a/test/req.acceptsLanguage.js +++ b/test/req.acceptsLanguage.js @@ -8,8 +8,8 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguage('en-us').should.be.ok; - req.acceptsLanguage('en').should.be.ok; + req.acceptsLanguage('en-us').should.be.ok() + req.acceptsLanguage('en').should.be.ok() res.end(); }); @@ -23,7 +23,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguage('es').should.not.be.ok; + req.acceptsLanguage('es').should.not.be.ok() res.end(); }); @@ -38,9 +38,9 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguage('en').should.be.ok; - req.acceptsLanguage('es').should.be.ok; - req.acceptsLanguage('jp').should.be.ok; + req.acceptsLanguage('en').should.be.ok() + req.acceptsLanguage('es').should.be.ok() + req.acceptsLanguage('jp').should.be.ok() res.end(); }); diff --git a/test/req.acceptsLanguages.js b/test/req.acceptsLanguages.js index 6a9cb3366bc..1d92f44b2b3 100644 --- a/test/req.acceptsLanguages.js +++ b/test/req.acceptsLanguages.js @@ -8,8 +8,8 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguages('en-us').should.be.ok; - req.acceptsLanguages('en').should.be.ok; + req.acceptsLanguages('en-us').should.be.ok() + req.acceptsLanguages('en').should.be.ok() res.end(); }); @@ -23,7 +23,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguages('es').should.not.be.ok; + req.acceptsLanguages('es').should.not.be.ok() res.end(); }); @@ -38,9 +38,9 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguages('en').should.be.ok; - req.acceptsLanguages('es').should.be.ok; - req.acceptsLanguages('jp').should.be.ok; + req.acceptsLanguages('en').should.be.ok() + req.acceptsLanguages('es').should.be.ok() + req.acceptsLanguages('jp').should.be.ok() res.end(); }); diff --git a/test/req.xhr.js b/test/req.xhr.js index cc8754ce4cf..1bbc247104d 100644 --- a/test/req.xhr.js +++ b/test/req.xhr.js @@ -8,7 +8,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.xhr.should.be.true; + req.xhr.should.be.true() res.end(); }); @@ -25,7 +25,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.xhr.should.be.true; + req.xhr.should.be.true() res.end(); }); @@ -42,7 +42,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.xhr.should.be.false; + req.xhr.should.be.false() res.end(); }); @@ -59,7 +59,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.xhr.should.be.false; + req.xhr.should.be.false() res.end(); }); diff --git a/test/res.sendFile.js b/test/res.sendFile.js index be3a23ebc2c..a3576d02196 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -108,7 +108,7 @@ describe('res', function(){ }); app.use(function (err, req, res, next) { - err.code.should.be.empty; + err.code.should.be.empty() cb(); }); @@ -224,7 +224,7 @@ describe('res', function(){ app.use(function (req, res) { setImmediate(function () { res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { - should(err).be.ok; + should(err).be.ok() err.code.should.equal('ECONNABORTED'); cb(); }); @@ -243,7 +243,7 @@ describe('res', function(){ app.use(function (req, res) { onFinished(res, function () { res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { - should(err).be.ok; + should(err).be.ok() err.code.should.equal('ECONNABORTED'); cb(); }); @@ -294,7 +294,7 @@ describe('res', function(){ app.use(function (req, res) { res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) { - should(err).be.ok; + should(err).be.ok() err.status.should.equal(404); res.send('got it'); }); @@ -348,7 +348,7 @@ describe('res', function(){ app.use(function (req, res) { setImmediate(function () { res.sendfile('test/fixtures/name.txt', function (err) { - should(err).be.ok; + should(err).be.ok() err.code.should.equal('ECONNABORTED'); cb(); }); @@ -367,7 +367,7 @@ describe('res', function(){ app.use(function (req, res) { onFinished(res, function () { res.sendfile('test/fixtures/name.txt', function (err) { - should(err).be.ok; + should(err).be.ok() err.code.should.equal('ECONNABORTED'); cb(); }); @@ -600,7 +600,7 @@ describe('res', function(){ }); app.use(function (err, req, res, next) { - err.code.should.be.empty; + err.code.should.be.empty() cb(); }); From 56e90e3c7267782febe35754806ce3f63b527485 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 5 Aug 2017 23:37:39 -0400 Subject: [PATCH 020/479] lint: add eslint rules that cover editorconfig --- .eslintignore | 2 ++ .eslintrc | 7 +++++++ .travis.yml | 4 +++- appveyor.yml | 1 + examples/content-negotiation/db.js | 2 +- examples/mvc/controllers/main/index.js | 2 +- examples/mvc/db.js | 2 +- examples/resource/index.js | 2 +- examples/route-separation/site.js | 2 +- examples/search/public/client.js | 2 +- examples/static-files/public/js/app.js | 2 +- examples/web-service/index.js | 2 +- package.json | 2 ++ test/acceptance/error-pages.js | 2 +- test/acceptance/error.js | 2 +- test/acceptance/route-map.js | 2 +- test/app.engine.js | 4 ++-- test/config.js | 12 ++++++------ 18 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000000..62562b74a3b --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +coverage +node_modules diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000000..8f51db362e8 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,7 @@ +{ + "rules": { + "eol-last": "error", + "indent": ["error", 2, { "SwitchCase": 1 }], + "no-trailing-spaces": "error" + } +} diff --git a/.travis.yml b/.travis.yml index 3e899dd2351..5926ca5650a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,5 +27,7 @@ before_install: # Update Node.js modules - "test ! -d node_modules || npm prune" - "test ! -d node_modules || npm rebuild" -script: "npm run-script test-ci" +script: + - "npm run test-ci" + - "npm run lint" after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls" diff --git a/appveyor.yml b/appveyor.yml index ed8d4aa9f05..9863c08e272 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,4 +22,5 @@ test_script: - node --version - npm --version - npm run test-ci + - npm run lint version: "{build}" diff --git a/examples/content-negotiation/db.js b/examples/content-negotiation/db.js index 8def2f5ad21..43fb04baa18 100644 --- a/examples/content-negotiation/db.js +++ b/examples/content-negotiation/db.js @@ -4,4 +4,4 @@ users.push({ name: 'Tobi' }); users.push({ name: 'Loki' }); users.push({ name: 'Jane' }); -module.exports = users; \ No newline at end of file +module.exports = users; diff --git a/examples/mvc/controllers/main/index.js b/examples/mvc/controllers/main/index.js index 83db90f6f20..031862d345e 100644 --- a/examples/mvc/controllers/main/index.js +++ b/examples/mvc/controllers/main/index.js @@ -1,3 +1,3 @@ exports.index = function(req, res){ res.redirect('/users'); -}; \ No newline at end of file +}; diff --git a/examples/mvc/db.js b/examples/mvc/db.js index 565fdfaa507..c992afcfd74 100644 --- a/examples/mvc/db.js +++ b/examples/mvc/db.js @@ -11,4 +11,4 @@ var users = exports.users = []; users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 }); users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 }); -users.push({ name: 'Nathan', pets: [], id: 2 }); \ No newline at end of file +users.push({ name: 'Nathan', pets: [], id: 2 }); diff --git a/examples/resource/index.js b/examples/resource/index.js index 9137167cdf9..0c2a7a32079 100644 --- a/examples/resource/index.js +++ b/examples/resource/index.js @@ -75,7 +75,7 @@ app.resource('/users', User); app.get('/', function(req, res){ res.send([ - '

Examples:

    ' + '

    Examples:

      ' , '
    • GET /users
    • ' , '
    • GET /users/1
    • ' , '
    • GET /users/3
    • ' diff --git a/examples/route-separation/site.js b/examples/route-separation/site.js index 698892cc89c..a3d20bc8a1f 100644 --- a/examples/route-separation/site.js +++ b/examples/route-separation/site.js @@ -1,3 +1,3 @@ exports.index = function(req, res){ res.render('index', { title: 'Route Separation Example' }); -}; \ No newline at end of file +}; diff --git a/examples/search/public/client.js b/examples/search/public/client.js index 0c198cc39fa..a7eeb6a75af 100644 --- a/examples/search/public/client.js +++ b/examples/search/public/client.js @@ -10,4 +10,4 @@ search.addEventListener('keyup', function(){ } }; xhr.send(); -}, false); \ No newline at end of file +}, false); diff --git a/examples/static-files/public/js/app.js b/examples/static-files/public/js/app.js index 19102815663..257cc5642cb 100644 --- a/examples/static-files/public/js/app.js +++ b/examples/static-files/public/js/app.js @@ -1 +1 @@ -foo \ No newline at end of file +foo diff --git a/examples/web-service/index.js b/examples/web-service/index.js index 694e121d91b..41747cfdc7f 100644 --- a/examples/web-service/index.js +++ b/examples/web-service/index.js @@ -61,7 +61,7 @@ var users = [ ]; var userRepos = { - tobi: [repos[0], repos[1]] + tobi: [repos[0], repos[1]] , loki: [repos[1]] , jane: [repos[2]] }; diff --git a/package.json b/package.json index 15e80f1934b..9f38f52bc1a 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "body-parser": "1.17.2", "cookie-parser": "~1.4.3", "ejs": "2.5.7", + "eslint": "2.13.1", "express-session": "1.15.3", "hbs": "4.0.1", "istanbul": "0.4.5", @@ -87,6 +88,7 @@ "lib/" ], "scripts": { + "lint": "eslint .", "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/", "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/", diff --git a/test/acceptance/error-pages.js b/test/acceptance/error-pages.js index 886cedcabe3..9af950178da 100644 --- a/test/acceptance/error-pages.js +++ b/test/acceptance/error-pages.js @@ -99,4 +99,4 @@ describe('error-pages', function(){ }) }) }) -}) \ No newline at end of file +}) diff --git a/test/acceptance/error.js b/test/acceptance/error.js index 6010f2e2ae4..6bdf099feed 100644 --- a/test/acceptance/error.js +++ b/test/acceptance/error.js @@ -26,4 +26,4 @@ describe('error', function(){ .expect(404,done) }) }) -}) \ No newline at end of file +}) diff --git a/test/acceptance/route-map.js b/test/acceptance/route-map.js index ae3eeea6507..0bd2a6d32e1 100644 --- a/test/acceptance/route-map.js +++ b/test/acceptance/route-map.js @@ -42,4 +42,4 @@ describe('route-map', function(){ .expect('delete 12\'s pet 2', done); }) }) -}) \ No newline at end of file +}) diff --git a/test/app.engine.js b/test/app.engine.js index 6d1ee1cacbf..b198292fa03 100644 --- a/test/app.engine.js +++ b/test/app.engine.js @@ -47,7 +47,7 @@ describe('app', function(){ done(); }) }) - + it('should work "view engine" setting', function(done){ var app = express(); @@ -62,7 +62,7 @@ describe('app', function(){ done(); }) }) - + it('should work "view engine" with leading "."', function(done){ var app = express(); diff --git a/test/config.js b/test/config.js index e298e76a5c5..17a02b7ebab 100644 --- a/test/config.js +++ b/test/config.js @@ -49,7 +49,7 @@ describe('config', function () { var app = express(); assert.strictEqual(app.get('foo'), undefined); }) - + it('should otherwise return the value', function(){ var app = express(); app.set('foo', 'bar'); @@ -125,7 +125,7 @@ describe('config', function () { assert.strictEqual(app.get('tobi'), true); }) }) - + describe('.disable()', function(){ it('should set the value to false', function(){ var app = express(); @@ -133,26 +133,26 @@ describe('config', function () { assert.strictEqual(app.get('tobi'), false); }) }) - + describe('.enabled()', function(){ it('should default to false', function(){ var app = express(); assert.strictEqual(app.enabled('foo'), false); }) - + it('should return true when set', function(){ var app = express(); app.set('foo', 'bar'); assert.strictEqual(app.enabled('foo'), true); }) }) - + describe('.disabled()', function(){ it('should default to true', function(){ var app = express(); assert.strictEqual(app.disabled('foo'), true); }) - + it('should return false when set', function(){ var app = express(); app.set('foo', 'bar'); From 1dbaae51ddb64c7397d19546bacb0792dbb7d59b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 5 Aug 2017 23:54:31 -0400 Subject: [PATCH 021/479] deps: update example dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9f38f52bc1a..ab6db290342 100644 --- a/package.json +++ b/package.json @@ -60,9 +60,10 @@ "after": "0.8.2", "body-parser": "1.17.2", "cookie-parser": "~1.4.3", + "cookie-session": "1.3.0", "ejs": "2.5.7", "eslint": "2.13.1", - "express-session": "1.15.3", + "express-session": "1.15.5", "hbs": "4.0.1", "istanbul": "0.4.5", "marked": "0.3.6", @@ -74,7 +75,6 @@ "should": "11.2.1", "supertest": "1.2.0", "connect-redis": "~2.4.1", - "cookie-session": "~1.2.0", "vhost": "~3.0.2" }, "engines": { From 44881fabe3680722368df75c66125fbd5f8ed569 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 00:18:57 -0400 Subject: [PATCH 022/479] docs: update collaborator guide for lint script --- Collaborator-Guide.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Collaborator-Guide.md b/Collaborator-Guide.md index 7c0d265dd0a..75d4e7c8f28 100644 --- a/Collaborator-Guide.md +++ b/Collaborator-Guide.md @@ -6,7 +6,7 @@ Open issues for the expressjs.com website in https://github.com/expressjs/expres ## PRs and Code contributions * Tests must pass. -* Follow the [JavaScript Standard Style](http://standardjs.com/). +* Follow the [JavaScript Standard Style](http://standardjs.com/) and `npm run lint`. * If you fix a bug, add a test. ## Branches @@ -27,7 +27,9 @@ a future release of Express. each new issue you work on, although not compulsory. 4. To run the test suite, first install the dependencies by running `npm install`, then run `npm test`. -5. If the tests pass, you can commit your changes to your fork and then create +5. Ensure your code is linted by running `npm run lint` -- fix any issue you + see listed. +6. If the tests pass, you can commit your changes to your fork and then create a pull request from there. Make sure to reference your issue from the pull request comments by including the issue number e.g. `#123`. From e0066227f787931bb0db09e76e007450d0f365b7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 00:19:32 -0400 Subject: [PATCH 023/479] lint: remove all unused varaibles --- .eslintrc | 3 ++- benchmarks/middleware.js | 1 - examples/view-constructor/index.js | 1 - lib/utils.js | 1 - lib/view.js | 1 - test/Route.js | 1 - test/app.listen.js | 1 - test/app.locals.js | 1 - test/app.router.js | 1 - test/req.host.js | 1 - test/req.hostname.js | 1 - test/req.range.js | 1 - test/res.download.js | 1 - test/res.format.js | 1 - test/res.send.js | 1 - test/res.sendFile.js | 4 ---- test/res.sendStatus.js | 1 - test/res.vary.js | 1 - 18 files changed, 2 insertions(+), 21 deletions(-) diff --git a/.eslintrc b/.eslintrc index 8f51db362e8..ad9c0ce9eb7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,6 +2,7 @@ "rules": { "eol-last": "error", "indent": ["error", 2, { "SwitchCase": 1 }], - "no-trailing-spaces": "error" + "no-trailing-spaces": "error", + "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }] } } diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js index 3aa7a8b4ac7..efbac12983a 100644 --- a/benchmarks/middleware.js +++ b/benchmarks/middleware.js @@ -1,5 +1,4 @@ -var http = require('http'); var express = require('..'); var app = express(); diff --git a/examples/view-constructor/index.js b/examples/view-constructor/index.js index 195d32db0eb..175a254e4ee 100644 --- a/examples/view-constructor/index.js +++ b/examples/view-constructor/index.js @@ -3,7 +3,6 @@ */ var express = require('../../'); -var http = require('http'); var GithubView = require('./github-view'); var md = require('marked').parse; diff --git a/lib/utils.js b/lib/utils.js index f418c5807c7..ae2a7f862d2 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -17,7 +17,6 @@ var contentType = require('content-type'); var deprecate = require('depd')('express'); var flatten = require('array-flatten'); var mime = require('send').mime; -var basename = require('path').basename; var etag = require('etag'); var proxyaddr = require('proxy-addr'); var qs = require('qs'); diff --git a/lib/view.js b/lib/view.js index 1728725d291..99d5aed7a07 100644 --- a/lib/view.js +++ b/lib/view.js @@ -16,7 +16,6 @@ var debug = require('debug')('express:view'); var path = require('path'); var fs = require('fs'); -var utils = require('./utils'); /** * Module variables. diff --git a/test/Route.js b/test/Route.js index d7a80bdbc01..8f90152d8c8 100644 --- a/test/Route.js +++ b/test/Route.js @@ -4,7 +4,6 @@ var should = require('should'); var express = require('../') , Route = express.Route , methods = require('methods') - , assert = require('assert'); describe('Route', function(){ it('should work without handlers', function(done) { diff --git a/test/app.listen.js b/test/app.listen.js index b6f68578934..a78d16e4e1a 100644 --- a/test/app.listen.js +++ b/test/app.listen.js @@ -1,6 +1,5 @@ var express = require('../') - , request = require('supertest'); describe('app.listen()', function(){ it('should wrap with an HTTP server', function(done){ diff --git a/test/app.locals.js b/test/app.locals.js index a8b022957a2..d8bfb5a9874 100644 --- a/test/app.locals.js +++ b/test/app.locals.js @@ -1,6 +1,5 @@ var express = require('../') - , request = require('supertest'); describe('app', function(){ describe('.locals(obj)', function(){ diff --git a/test/app.router.js b/test/app.router.js index 95680f9139c..28561c2fbcd 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -39,7 +39,6 @@ describe('app.router', function(){ it('should include ' + method.toUpperCase(), function(done){ var app = express(); - var calls = []; app[method]('/foo', function(req, res){ if ('head' == method) { diff --git a/test/req.host.js b/test/req.host.js index 8fa3409054f..7bb0b27acf8 100644 --- a/test/req.host.js +++ b/test/req.host.js @@ -1,7 +1,6 @@ var express = require('../') , request = require('supertest') - , assert = require('assert'); describe('req', function(){ describe('.host', function(){ diff --git a/test/req.hostname.js b/test/req.hostname.js index 65c2be81a1f..816cd597990 100644 --- a/test/req.hostname.js +++ b/test/req.hostname.js @@ -1,7 +1,6 @@ var express = require('../') , request = require('supertest') - , assert = require('assert'); describe('req', function(){ describe('.hostname', function(){ diff --git a/test/req.range.js b/test/req.range.js index 09459d1e127..5443c0658d2 100644 --- a/test/req.range.js +++ b/test/req.range.js @@ -1,5 +1,4 @@ -var assert = require('assert'); var express = require('..'); var request = require('supertest') diff --git a/test/res.download.js b/test/res.download.js index 0671d8318c4..fad56ee256a 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -89,7 +89,6 @@ describe('res', function(){ it('should remove Content-Disposition', function(done){ var app = express() - , calls = 0; app.use(function (req, res, next) { res.download('test/fixtures/foobar.html', function(err){ diff --git a/test/res.format.js b/test/res.format.js index 2b0dfd517e7..3c1d095b426 100644 --- a/test/res.format.js +++ b/test/res.format.js @@ -1,7 +1,6 @@ var express = require('../') , request = require('supertest') - , utils = require('../lib/utils') , assert = require('assert'); var app1 = express(); diff --git a/test/res.send.js b/test/res.send.js index f2e7d759c15..88d231eab55 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -1,5 +1,4 @@ -var assert = require('assert'); var express = require('..'); var methods = require('methods'); var request = require('supertest'); diff --git a/test/res.sendFile.js b/test/res.sendFile.js index a3576d02196..ff4b1cb2dd1 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -446,12 +446,10 @@ describe('res', function(){ it('should invoke the callback on 403', function(done){ var app = express() - , calls = 0; app.use(function(req, res){ res.sendfile('test/fixtures/foo/../user.html', function(err){ assert(!res.headersSent); - ++calls; res.send(err.message); }); }); @@ -464,7 +462,6 @@ describe('res', function(){ it('should invoke the callback on socket error', function(done){ var app = express() - , calls = 0; app.use(function(req, res){ res.sendfile('test/fixtures/user.html', function(err){ @@ -715,7 +712,6 @@ describe('res', function(){ describe('with non-GET', function(){ it('should still serve', function(done){ var app = express() - , calls = 0; app.use(function(req, res){ res.sendfile(path.join(__dirname, '/fixtures/name.txt')) diff --git a/test/res.sendStatus.js b/test/res.sendStatus.js index a97e1bf8d81..c355bc408f3 100644 --- a/test/res.sendStatus.js +++ b/test/res.sendStatus.js @@ -1,5 +1,4 @@ -var assert = require('assert') var express = require('..') var request = require('supertest') diff --git a/test/res.vary.js b/test/res.vary.js index 9a2edd24c09..9d39a341c0b 100644 --- a/test/res.vary.js +++ b/test/res.vary.js @@ -1,5 +1,4 @@ -var assert = require('assert'); var express = require('..'); var request = require('supertest'); var utils = require('./support/utils'); From e2d725e01620fc3c8b3720e5521a124836e32cb2 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 02:37:10 -0400 Subject: [PATCH 024/479] deps: send@0.15.4 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c67fd941745..422d52dede3 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,10 @@ unreleased - Fix array argument being altered - deps: ipaddr.js@1.4.0 * deps: qs@6.5.0 + * deps: send@0.15.4 + - deps: debug@2.6.8 + - deps: depd@~1.1.1 + - deps: http-errors@~1.6.2 4.15.3 / 2017-05-16 =================== diff --git a/package.json b/package.json index ab6db290342..5c9e94f4898 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", - "send": "0.15.3", + "send": "0.15.4", "serve-static": "1.12.3", "setprototypeof": "1.0.3", "statuses": "~1.3.1", From a50f1098d014e2393e2d5f4beae37a85830c203d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 02:38:02 -0400 Subject: [PATCH 025/479] deps: serve-static@1.12.4 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 422d52dede3..91707e6c03d 100644 --- a/History.md +++ b/History.md @@ -14,6 +14,8 @@ unreleased - deps: debug@2.6.8 - deps: depd@~1.1.1 - deps: http-errors@~1.6.2 + * deps: serve-static@1.12.4 + - deps: send@0.15.4 4.15.3 / 2017-05-16 =================== diff --git a/package.json b/package.json index 5c9e94f4898..d5b9ae405df 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.4", - "serve-static": "1.12.3", + "serve-static": "1.12.4", "setprototypeof": "1.0.3", "statuses": "~1.3.1", "type-is": "~1.6.15", From a4bd4373b2c3b2521ee4c499cb8e90e98f78bfa5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 22:03:53 -0400 Subject: [PATCH 026/479] 4.15.4 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 91707e6c03d..f297e3b075c 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.15.4 / 2017-08-06 +=================== * deps: debug@2.6.8 * deps: depd@~1.1.1 diff --git a/package.json b/package.json index d5b9ae405df..d8ec444d539 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.15.3", + "version": "4.15.4", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 48817a798f3820bbe252d30d33bd701779511dc5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 17 Aug 2017 22:03:40 -0400 Subject: [PATCH 027/479] build: remove minor pin for nightly --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5926ca5650a..3dbeb41fcda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ node_js: - "7.10" matrix: include: - - node_js: "8.0" + - node_js: "8" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" allow_failures: # Allow the nightly installs to fail From 78e55108e40ce8ce751baa10324f48a6bb21b47e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 13 Sep 2017 20:03:42 -0400 Subject: [PATCH 028/479] build: mocha@3.5.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d8ec444d539..2eec2887a7b 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "istanbul": "0.4.5", "marked": "0.3.6", "method-override": "2.3.9", - "mocha": "3.5.0", + "mocha": "3.5.3", "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", From b208b24f8323930419d9b5bbe0f442b36852dc36 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 13 Sep 2017 20:09:33 -0400 Subject: [PATCH 029/479] build: should@13.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2eec2887a7b..5068a5d6144 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", - "should": "11.2.1", + "should": "13.0.1", "supertest": "1.2.0", "connect-redis": "~2.4.1", "vhost": "~3.0.2" From de5fb62b1ac8d02efcb7931ef12936cb0a954307 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 17 Sep 2017 20:13:05 -0400 Subject: [PATCH 030/479] deps: update example dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5068a5d6144..a02a6943d37 100644 --- a/package.json +++ b/package.json @@ -58,9 +58,9 @@ }, "devDependencies": { "after": "0.8.2", - "body-parser": "1.17.2", + "body-parser": "1.18.1", "cookie-parser": "~1.4.3", - "cookie-session": "1.3.0", + "cookie-session": "1.3.1", "ejs": "2.5.7", "eslint": "2.13.1", "express-session": "1.15.5", From 9e067ad2cb96f23f7997758a7f5a3c69ada03c12 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Sep 2017 20:45:32 -0400 Subject: [PATCH 031/479] deps: fresh@0.5.2 --- History.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index f297e3b075c..e7e37e92e24 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,11 @@ +unreleased +========== + + * deps: fresh@0.5.2 + - Fix handling of modified headers with invalid dates + - perf: improve ETag match loop + - perf: improve `If-None-Match` token parsing + 4.15.4 / 2017-08-06 =================== diff --git a/package.json b/package.json index a02a6943d37..87e4ee8eae4 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "escape-html": "~1.0.3", "etag": "~1.8.0", "finalhandler": "~1.0.4", - "fresh": "0.5.0", + "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", From 9e0fa7f1ca2efe768e91ee84534f837d2cff243a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Sep 2017 20:46:42 -0400 Subject: [PATCH 032/479] deps: send@0.15.5 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index e7e37e92e24..83cd43185e5 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,10 @@ unreleased - Fix handling of modified headers with invalid dates - perf: improve ETag match loop - perf: improve `If-None-Match` token parsing + * deps: send@0.15.5 + - Fix handling of modified headers with invalid dates + - deps: etag@~1.8.1 + - deps: fresh@0.5.2 4.15.4 / 2017-08-06 =================== diff --git a/package.json b/package.json index 87e4ee8eae4..2ad3c5e2a99 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", - "send": "0.15.4", + "send": "0.15.5", "serve-static": "1.12.4", "setprototypeof": "1.0.3", "statuses": "~1.3.1", From 961dbff904d3e6b1b10cfe6741506ae851d272ff Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Sep 2017 20:48:23 -0400 Subject: [PATCH 033/479] deps: serve-static@1.12.5 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 83cd43185e5..95fad3d3d97 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,9 @@ unreleased - Fix handling of modified headers with invalid dates - deps: etag@~1.8.1 - deps: fresh@0.5.2 + * deps: serve-static@1.12.5 + - deps: parseurl@~1.3.2 + - deps: send@0.15.5 4.15.4 / 2017-08-06 =================== diff --git a/package.json b/package.json index 2ad3c5e2a99..4bb68915b8e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.5", - "serve-static": "1.12.4", + "serve-static": "1.12.5", "setprototypeof": "1.0.3", "statuses": "~1.3.1", "type-is": "~1.6.15", From d7da22550da484ddcdf77623272b64c36030b216 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Sep 2017 20:49:14 -0400 Subject: [PATCH 034/479] build: should@13.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4bb68915b8e..97f94459b83 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", - "should": "13.0.1", + "should": "13.1.0", "supertest": "1.2.0", "connect-redis": "~2.4.1", "vhost": "~3.0.2" From 19a2eeb47697feecae5960a726fb5b7ae2c7644b Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Tue, 21 Mar 2017 14:53:42 -0700 Subject: [PATCH 035/479] tests: check render error without engine-specific message closes #3251 --- test/app.render.js | 10 ++++------ test/res.render.js | 10 ++++++---- test/support/tmpl.js | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/test/app.render.js b/test/app.render.js index 1485098f582..54f6c2ca82d 100644 --- a/test/app.render.js +++ b/test/app.render.js @@ -97,12 +97,10 @@ describe('app', function(){ app.set('views', path.join(__dirname, 'fixtures')) - app.render('user.tmpl', function (err, str) { - // nextTick to prevent cyclic - process.nextTick(function(){ - err.message.should.match(/Cannot read property '[^']+' of undefined/); - done(); - }); + app.render('user.tmpl', function (err) { + assert.ok(err) + assert.equal(err.name, 'RenderError') + done() }) }) }) diff --git a/test/res.render.js b/test/res.render.js index 2e3a16f1370..e19e8cc542b 100644 --- a/test/res.render.js +++ b/test/res.render.js @@ -105,12 +105,12 @@ describe('res', function(){ }); app.use(function(err, req, res, next){ - res.end(err.message); + res.status(500).send('got error: ' + err.name) }); request(app) .get('/') - .expect(/Cannot read property '[^']+' of undefined/, done); + .expect(500, 'got error: RenderError', done) }) }) @@ -329,13 +329,15 @@ describe('res', function(){ app.use(function(req, res){ res.render('user.tmpl', function (err) { - res.end(err.message); + if (err) { + res.status(500).send('got error: ' + err.name) + } }); }); request(app) .get('/') - .expect(/Cannot read property '[^']+' of undefined/, done); + .expect(500, 'got error: RenderError', done) }) }) }) diff --git a/test/support/tmpl.js b/test/support/tmpl.js index 2e8bec86388..bab65669d33 100644 --- a/test/support/tmpl.js +++ b/test/support/tmpl.js @@ -13,6 +13,7 @@ module.exports = function renderFile(fileName, options, callback) { str = str.replace(variableRegExp, generateVariableLookup(options)); } catch (e) { err = e; + err.name = 'RenderError' } callback(err, str); From 9395db4c22567d09f19ac7cd629e23908784ec6d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 22 Sep 2017 20:25:18 -0400 Subject: [PATCH 036/479] deps: debug@2.6.9 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 95fad3d3d97..5bf412451be 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * deps: debug@2.6.9 * deps: fresh@0.5.2 - Fix handling of modified headers with invalid dates - perf: improve ETag match loop diff --git a/package.json b/package.json index 97f94459b83..7f465d0e642 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "content-type": "~1.0.2", "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "2.6.8", + "debug": "2.6.9", "depd": "~1.1.1", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", From bd1672f0a45e2722126a05723aca68cbd65e3f74 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 22 Sep 2017 20:26:30 -0400 Subject: [PATCH 037/479] deps: finalhandler@~1.0.6 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 5bf412451be..e4770be6885 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,9 @@ unreleased ========== * deps: debug@2.6.9 + * deps: finalhandler@~1.0.6 + - deps: debug@2.6.9 + - deps: parseurl@~1.3.2 * deps: fresh@0.5.2 - Fix handling of modified headers with invalid dates - perf: improve ETag match loop diff --git a/package.json b/package.json index 7f465d0e642..3afcec06718 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "encodeurl": "~1.0.1", "escape-html": "~1.0.3", "etag": "~1.8.0", - "finalhandler": "~1.0.4", + "finalhandler": "~1.0.6", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From 7137bf567db674fa5a93b71fffda09e7ac4ec73c Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 22 Sep 2017 20:27:37 -0400 Subject: [PATCH 038/479] deps: send@0.15.6 --- History.md | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index e4770be6885..3d2bb66505c 100644 --- a/History.md +++ b/History.md @@ -9,10 +9,12 @@ unreleased - Fix handling of modified headers with invalid dates - perf: improve ETag match loop - perf: improve `If-None-Match` token parsing - * deps: send@0.15.5 + * deps: send@0.15.6 - Fix handling of modified headers with invalid dates + - deps: debug@2.6.9 - deps: etag@~1.8.1 - deps: fresh@0.5.2 + - perf: improve `If-Match` token parsing * deps: serve-static@1.12.5 - deps: parseurl@~1.3.2 - deps: send@0.15.5 diff --git a/package.json b/package.json index 3afcec06718..16b11d6fdb1 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", - "send": "0.15.5", + "send": "0.15.6", "serve-static": "1.12.5", "setprototypeof": "1.0.3", "statuses": "~1.3.1", From 40435ec99779b08202f9f139c9a0a7d64e941b40 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 22 Sep 2017 20:28:52 -0400 Subject: [PATCH 039/479] deps: serve-static@1.12.6 --- History.md | 5 +++-- package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 3d2bb66505c..fa520920d7c 100644 --- a/History.md +++ b/History.md @@ -15,9 +15,10 @@ unreleased - deps: etag@~1.8.1 - deps: fresh@0.5.2 - perf: improve `If-Match` token parsing - * deps: serve-static@1.12.5 + * deps: serve-static@1.12.6 - deps: parseurl@~1.3.2 - - deps: send@0.15.5 + - deps: send@0.15.6 + - perf: improve slash collapsing 4.15.4 / 2017-08-06 =================== diff --git a/package.json b/package.json index 16b11d6fdb1..bf48b83c7cc 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.6", - "serve-static": "1.12.5", + "serve-static": "1.12.6", "setprototypeof": "1.0.3", "statuses": "~1.3.1", "type-is": "~1.6.15", From ea3d60565242c47be97088ead2708d7b88390858 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 01:04:38 -0400 Subject: [PATCH 040/479] 4.15.5 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index fa520920d7c..707677eb17a 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.15.5 / 2017-09-24 +=================== * deps: debug@2.6.9 * deps: finalhandler@~1.0.6 diff --git a/package.json b/package.json index bf48b83c7cc..954c20b3c30 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.15.4", + "version": "4.15.5", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 94fdb674b1df2e36d389fce51f5e07071f807adb Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 20:57:54 -0400 Subject: [PATCH 041/479] build: support Node.js 8.x --- .gitignore | 1 + .travis.yml | 4 ++++ appveyor.yml | 2 ++ 3 files changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 9723e60591d..5fee6a2dc97 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ Desktop.ini # npm node_modules +package-lock.json *.log *.gz diff --git a/.travis.yml b/.travis.yml index 3dbeb41fcda..3e487a6f186 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ node_js: - "5.12" - "6.11" - "7.10" + - "8.4" matrix: include: - node_js: "8" @@ -21,6 +22,9 @@ cache: directories: - node_modules before_install: + # Skip updating shrinkwrap / lock + - "npm config set shrinkwrap false" + # Remove all non-test dependencies - "npm rm --save-dev connect-redis" diff --git a/appveyor.yml b/appveyor.yml index 9863c08e272..193660af715 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,10 +9,12 @@ environment: - nodejs_version: "5.12" - nodejs_version: "6.11" - nodejs_version: "7.10" + - nodejs_version: "8.4" cache: - node_modules install: - ps: Install-Product node $env:nodejs_version + - npm config set shrinkwrap false - npm rm --save-dev connect-redis - if exist node_modules npm prune - if exist node_modules npm rebuild From c3fb7e5adc1fd40e301ed1e25f0d5b5a393d0295 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 21:06:00 -0400 Subject: [PATCH 042/479] build: test against Node.js 9.x nightly --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3e487a6f186..855168ff540 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ matrix: include: - node_js: "8" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" + - node_js: "9" + env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" allow_failures: # Allow the nightly installs to fail - env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" From 80f1ea9bec3c5aedb08a6917ecc24fb8d22b707d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 21:11:33 -0400 Subject: [PATCH 043/479] Improve error message when autoloading invalid view engine fixes #3403 --- History.md | 5 +++++ lib/view.js | 10 +++++++++- test/fixtures/broken.send | 0 test/res.render.js | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/broken.send diff --git a/History.md b/History.md index 707677eb17a..765f050318a 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Improve error message when autoloading invalid view engine + 4.15.5 / 2017-09-24 =================== diff --git a/lib/view.js b/lib/view.js index 99d5aed7a07..cf101caeab9 100644 --- a/lib/view.js +++ b/lib/view.js @@ -76,7 +76,15 @@ function View(name, options) { // load engine var mod = this.ext.substr(1) debug('require "%s"', mod) - opts.engines[this.ext] = require(mod).__express + + // default engine export + var fn = require(mod).__express + + if (typeof fn !== 'function') { + throw new Error('Module "' + mod + '" does not provide a view engine.') + } + + opts.engines[this.ext] = fn } // store loaded engine diff --git a/test/fixtures/broken.send b/test/fixtures/broken.send new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/res.render.js b/test/res.render.js index e19e8cc542b..643a57002a0 100644 --- a/test/res.render.js +++ b/test/res.render.js @@ -35,6 +35,20 @@ describe('res', function(){ .expect('

      tobi

      ', done); }) + it('should error without "view engine" set and file extension to a non-engine module', function (done) { + var app = createApp() + + app.locals.user = { name: 'tobi' } + + app.use(function (req, res) { + res.render(path.join(__dirname, 'fixtures', 'broken.send')) + }) + + request(app) + .get('/') + .expect(500, /does not provide a view engine/, done) + }) + it('should error without "view engine" set and no file extension', function (done) { var app = createApp(); From 48940e61202be07677659b3b9d87c967fc4e8bdc Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 21:12:47 -0400 Subject: [PATCH 044/479] Skip Buffer encoding when not generating ETag for small response --- History.md | 1 + lib/response.js | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/History.md b/History.md index 765f050318a..83ba2cc5d76 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * Improve error message when autoloading invalid view engine + * Skip `Buffer` encoding when not generating ETag for small response 4.15.5 / 2017-09-24 =================== diff --git a/lib/response.js b/lib/response.js index b852a60e2f5..e34af49d693 100644 --- a/lib/response.js +++ b/lib/response.js @@ -106,7 +106,6 @@ res.links = function(links){ res.send = function send(body) { var chunk = body; var encoding; - var len; var req = this.req; var type; @@ -171,23 +170,33 @@ res.send = function send(body) { } } + // determine if ETag should be generated + var etagFn = app.get('etag fn') + var generateETag = !this.get('ETag') && typeof etagFn === 'function' + // populate Content-Length + var len if (chunk !== undefined) { - if (!Buffer.isBuffer(chunk)) { - // convert chunk to Buffer; saves later double conversions + if (!generateETag && chunk.length < 1000) { + // just calculate length when no ETag + small chunk + len = Buffer.byteLength(chunk, encoding) + } else if (!Buffer.isBuffer(chunk)) { + // convert chunk to Buffer and calculate chunk = new Buffer(chunk, encoding); encoding = undefined; + len = chunk.length + } else { + // get length of Buffer + len = chunk.length } - len = chunk.length; this.set('Content-Length', len); } // populate ETag var etag; - var generateETag = len !== undefined && app.get('etag fn'); - if (typeof generateETag === 'function' && !this.get('ETag')) { - if ((etag = generateETag(chunk, encoding))) { + if (generateETag && len !== undefined) { + if ((etag = etagFn(chunk, encoding))) { this.set('ETag', etag); } } From 550043c21727674a9d00c30504beb95cfbd7bbba Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 21:14:00 -0400 Subject: [PATCH 045/479] deps: setprototypeof@1.1.0 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 83ba2cc5d76..e8fcafa4149 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,7 @@ unreleased * Improve error message when autoloading invalid view engine * Skip `Buffer` encoding when not generating ETag for small response + * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 =================== diff --git a/package.json b/package.json index 954c20b3c30..cd94d1cf94a 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "range-parser": "~1.2.0", "send": "0.15.6", "serve-static": "1.12.6", - "setprototypeof": "1.0.3", + "setprototypeof": "1.1.0", "statuses": "~1.3.1", "type-is": "~1.6.15", "utils-merge": "1.0.0", From 9a99c152703048591c031bd10d2a2e3ca55ebcac Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 27 Sep 2017 21:28:25 -0400 Subject: [PATCH 046/479] deps: accepts@~1.3.4 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index e8fcafa4149..0c206496496 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,8 @@ unreleased * Improve error message when autoloading invalid view engine * Skip `Buffer` encoding when not generating ETag for small response + * deps: accepts@~1.3.4 + - deps: mime-types@~2.1.16 * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index cd94d1cf94a..9351cf2e05b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "api" ], "dependencies": { - "accepts": "~1.3.3", + "accepts": "~1.3.4", "array-flatten": "1.1.1", "content-disposition": "0.5.2", "content-type": "~1.0.2", From 70589c3aef6fb64ce396848e78ca7ea0768d2d5d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 27 Sep 2017 21:30:08 -0400 Subject: [PATCH 047/479] deps: content-type@~1.0.4 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 0c206496496..0f6f82bffef 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,9 @@ unreleased * Skip `Buffer` encoding when not generating ETag for small response * deps: accepts@~1.3.4 - deps: mime-types@~2.1.16 + * deps: content-type@~1.0.4 + - perf: remove argument reassignment + - perf: skip parameter parsing when no parameters * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index 9351cf2e05b..5febf1048e5 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "accepts": "~1.3.4", "array-flatten": "1.1.1", "content-disposition": "0.5.2", - "content-type": "~1.0.2", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", From e62bb8bf9f68382414cdd7997fe661de4647c987 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 27 Sep 2017 21:30:43 -0400 Subject: [PATCH 048/479] deps: etag@~1.8.1 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 0f6f82bffef..077eec931b5 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,8 @@ unreleased * deps: content-type@~1.0.4 - perf: remove argument reassignment - perf: skip parameter parsing when no parameters + * deps: etag@~1.8.1 + - perf: replace regular expression with substring * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index 5febf1048e5..78834c50721 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "depd": "~1.1.1", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", - "etag": "~1.8.0", + "etag": "~1.8.1", "finalhandler": "~1.0.6", "fresh": "0.5.2", "merge-descriptors": "1.0.1", From ad7d96db479e6e9d93ab4848d5fe163905e123ed Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 27 Sep 2017 21:31:39 -0400 Subject: [PATCH 049/479] deps: qs@6.5.1 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 077eec931b5..012d8a2ffed 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,8 @@ unreleased - perf: skip parameter parsing when no parameters * deps: etag@~1.8.1 - perf: replace regular expression with substring + * deps: qs@6.5.1 + - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index 78834c50721..0bbfeb8e6ea 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "parseurl": "~1.3.1", "path-to-regexp": "0.1.7", "proxy-addr": "~1.1.5", - "qs": "6.5.0", + "qs": "6.5.1", "range-parser": "~1.2.0", "send": "0.15.6", "serve-static": "1.12.6", From 5cc761c86593f2e87c7a9dac02135548096bb952 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 00:04:47 -0400 Subject: [PATCH 050/479] deps: parseurl@~1.3.2 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 012d8a2ffed..2c9866dbb5d 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,9 @@ unreleased - perf: skip parameter parsing when no parameters * deps: etag@~1.8.1 - perf: replace regular expression with substring + * deps: parseurl@~1.3.2 + - perf: reduce overhead for full URLs + - perf: unroll the "fast-path" `RegExp` * deps: qs@6.5.1 - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 diff --git a/package.json b/package.json index 0bbfeb8e6ea..4330b141f08 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", - "parseurl": "~1.3.1", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", "proxy-addr": "~1.1.5", "qs": "6.5.1", From 673d51f4f0fa83f6b663ed6f9f0426940d07664b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 00:19:30 -0400 Subject: [PATCH 051/479] deps: utils-merge@1.0.1 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 2c9866dbb5d..eb01929864f 100644 --- a/History.md +++ b/History.md @@ -16,6 +16,7 @@ unreleased * deps: qs@6.5.1 - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 + * deps: utils-merge@1.0.1 4.15.5 / 2017-09-24 =================== diff --git a/package.json b/package.json index 4330b141f08..1298b943275 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "setprototypeof": "1.1.0", "statuses": "~1.3.1", "type-is": "~1.6.15", - "utils-merge": "1.0.0", + "utils-merge": "1.0.1", "vary": "~1.1.1" }, "devDependencies": { From c2f4fb535688eaec14c713190a4ab881e195a41a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 00:42:05 -0400 Subject: [PATCH 052/479] deps: finalhandler@1.1.0 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index eb01929864f..c4b34a76439 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,8 @@ unreleased - perf: skip parameter parsing when no parameters * deps: etag@~1.8.1 - perf: replace regular expression with substring + * deps: finalhandler@1.1.0 + - Use `res.headersSent` when available * deps: parseurl@~1.3.2 - perf: reduce overhead for full URLs - perf: unroll the "fast-path" `RegExp` diff --git a/package.json b/package.json index 1298b943275..ea49823cbbf 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "encodeurl": "~1.0.1", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.0.6", + "finalhandler": "1.1.0", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From 02a9d5fb28e313fd94ee5ec24fe5da02fbc0d6eb Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 01:18:04 -0400 Subject: [PATCH 053/479] deps: proxy-addr@~2.0.2 closes #3432 --- History.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c4b34a76439..4352f26acdf 100644 --- a/History.md +++ b/History.md @@ -15,6 +15,11 @@ unreleased * deps: parseurl@~1.3.2 - perf: reduce overhead for full URLs - perf: unroll the "fast-path" `RegExp` + * deps: proxy-addr@~2.0.2 + - Fix trimming leading / trailing OWS in `X-Forwarded-For` + - deps: forwarded@~0.1.2 + - deps: ipaddr.js@1.5.2 + - perf: reduce overhead when no `X-Forwarded-For` header * deps: qs@6.5.1 - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 diff --git a/package.json b/package.json index ea49823cbbf..219e21fe23b 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~1.1.5", + "proxy-addr": "~2.0.2", "qs": "6.5.1", "range-parser": "~1.2.0", "send": "0.15.6", From d9d09b8b9041504b645f3173ca70ef173c7e1563 Mon Sep 17 00:00:00 2001 From: Lawrence Page Date: Thu, 18 May 2017 11:04:27 -0700 Subject: [PATCH 054/479] perf: re-use options object when generating ETags closes #3313 closes #3314 --- History.md | 1 + lib/utils.js | 35 +++++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/History.md b/History.md index 4352f26acdf..4b4f0dd5cbf 100644 --- a/History.md +++ b/History.md @@ -24,6 +24,7 @@ unreleased - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 * deps: utils-merge@1.0.1 + * perf: re-use options object when generating ETags 4.15.5 / 2017-09-24 =================== diff --git a/lib/utils.js b/lib/utils.js index ae2a7f862d2..80f4edae3a3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -31,13 +31,7 @@ var querystring = require('querystring'); * @api private */ -exports.etag = function (body, encoding) { - var buf = !Buffer.isBuffer(body) - ? new Buffer(body, encoding) - : body; - - return etag(buf, {weak: false}); -}; +exports.etag = createETagGenerator({ weak: false }) /** * Return weak ETag for `body`. @@ -48,13 +42,7 @@ exports.etag = function (body, encoding) { * @api private */ -exports.wetag = function wetag(body, encoding){ - var buf = !Buffer.isBuffer(body) - ? new Buffer(body, encoding) - : body; - - return etag(buf, {weak: true}); -}; +exports.wetag = createETagGenerator({ weak: true }) /** * Check if `path` looks absolute. @@ -273,6 +261,25 @@ exports.setCharset = function setCharset(type, charset) { return contentType.format(parsed); }; +/** + * Create an ETag generator function, generating ETags with + * the given options. + * + * @param {object} options + * @return {function} + * @private + */ + +function createETagGenerator (options) { + return function generateETag (body, encoding) { + var buf = !Buffer.isBuffer(body) + ? new Buffer(body, encoding) + : body + + return etag(buf, options) + } +} + /** * Parse an extended query string with qs. * From fa272edf843a31aa242390d46935437451707d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hrvoje=20=C5=A0imi=C4=87?= Date: Wed, 27 Sep 2017 15:17:03 +0200 Subject: [PATCH 055/479] docs: fix typo in jsdoc comment closes #3430 --- lib/application.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/application.js b/lib/application.js index 1abe8d08f58..8097d81a3b9 100644 --- a/lib/application.js +++ b/lib/application.js @@ -338,7 +338,7 @@ app.param = function param(name, fn) { * Assign `setting` to `val`, or return `setting`'s value. * * app.set('foo', 'bar'); - * app.get('foo'); + * app.set('foo'); * // => "bar" * * Mounted servers inherit their parent server's settings. From 12c37124689380837b24a7ed962432596237b440 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 08:26:39 -0400 Subject: [PATCH 056/479] Use safe-buffer for improved Buffer API --- History.md | 1 + benchmarks/middleware.js | 4 +--- lib/response.js | 5 +++-- lib/utils.js | 3 ++- package.json | 1 + test/res.attachment.js | 3 ++- test/res.send.js | 12 ++++++------ test/utils.js | 7 +++---- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/History.md b/History.md index 4b4f0dd5cbf..18f5e5da371 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,7 @@ unreleased * Improve error message when autoloading invalid view engine * Skip `Buffer` encoding when not generating ETag for small response + * Use `safe-buffer` for improved Buffer API * deps: accepts@~1.3.4 - deps: mime-types@~2.1.16 * deps: content-type@~1.0.4 diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js index efbac12983a..df4df2c5ac5 100644 --- a/benchmarks/middleware.js +++ b/benchmarks/middleware.js @@ -13,10 +13,8 @@ while (n--) { }); } -var body = new Buffer('Hello World'); - app.use(function(req, res, next){ - res.send(body); + res.send('Hello World') }); app.listen(3333); diff --git a/lib/response.js b/lib/response.js index e34af49d693..285daf4fb4b 100644 --- a/lib/response.js +++ b/lib/response.js @@ -12,6 +12,7 @@ * @private */ +var Buffer = require('safe-buffer').Buffer var contentDisposition = require('content-disposition'); var deprecate = require('depd')('express'); var encodeUrl = require('encodeurl'); @@ -95,7 +96,7 @@ res.links = function(links){ * * Examples: * - * res.send(new Buffer('wahoo')); + * res.send(Buffer.from('wahoo')); * res.send({ some: 'json' }); * res.send('

      some html

      '); * @@ -182,7 +183,7 @@ res.send = function send(body) { len = Buffer.byteLength(chunk, encoding) } else if (!Buffer.isBuffer(chunk)) { // convert chunk to Buffer and calculate - chunk = new Buffer(chunk, encoding); + chunk = Buffer.from(chunk, encoding) encoding = undefined; len = chunk.length } else { diff --git a/lib/utils.js b/lib/utils.js index 80f4edae3a3..bd81ac7f6d9 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -12,6 +12,7 @@ * @api private */ +var Buffer = require('safe-buffer').Buffer var contentDisposition = require('content-disposition'); var contentType = require('content-type'); var deprecate = require('depd')('express'); @@ -273,7 +274,7 @@ exports.setCharset = function setCharset(type, charset) { function createETagGenerator (options) { return function generateETag (body, encoding) { var buf = !Buffer.isBuffer(body) - ? new Buffer(body, encoding) + ? Buffer.from(body, encoding) : body return etag(buf, options) diff --git a/package.json b/package.json index 219e21fe23b..dbbd7810042 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "proxy-addr": "~2.0.2", "qs": "6.5.1", "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", "send": "0.15.6", "serve-static": "1.12.6", "setprototypeof": "1.1.0", diff --git a/test/res.attachment.js b/test/res.attachment.js index 662b1dd4e01..4c3d4aa2f1b 100644 --- a/test/res.attachment.js +++ b/test/res.attachment.js @@ -1,4 +1,5 @@ +var Buffer = require('safe-buffer').Buffer var express = require('../') , request = require('supertest'); @@ -36,7 +37,7 @@ describe('res', function(){ app.use(function(req, res){ res.attachment('/path/to/image.png'); - res.send(new Buffer(4)); + res.send(Buffer.alloc(4, '.')) }); request(app) diff --git a/test/res.send.js b/test/res.send.js index 88d231eab55..7aa8d7d90e2 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -1,4 +1,5 @@ +var Buffer = require('safe-buffer').Buffer var express = require('..'); var methods = require('methods'); var request = require('supertest'); @@ -166,7 +167,7 @@ describe('res', function(){ var app = express(); app.use(function(req, res){ - res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(new Buffer('hi')); + res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(Buffer.from('hi')) }); request(app) @@ -181,7 +182,7 @@ describe('res', function(){ var app = express(); app.use(function(req, res){ - res.send(new Buffer('hello')); + res.send(Buffer.from('hello')) }); request(app) @@ -194,8 +195,7 @@ describe('res', function(){ var app = express(); app.use(function (req, res) { - var str = Array(1000).join('-'); - res.send(new Buffer(str)); + res.send(Buffer.alloc(999, '-')) }); request(app) @@ -208,7 +208,7 @@ describe('res', function(){ var app = express(); app.use(function(req, res){ - res.set('Content-Type', 'text/plain').send(new Buffer('hey')); + res.set('Content-Type', 'text/plain').send(Buffer.from('hey')) }); request(app) @@ -512,7 +512,7 @@ describe('res', function(){ app.set('etag', function (body, encoding) { var chunk = !Buffer.isBuffer(body) - ? new Buffer(body, encoding) + ? Buffer.from(body, encoding) : body; chunk.toString().should.equal('hello, world!'); return '"custom"'; diff --git a/test/utils.js b/test/utils.js index c49019fe126..b51d223af97 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,5 +1,6 @@ var assert = require('assert'); +var Buffer = require('safe-buffer').Buffer var utils = require('../lib/utils'); describe('utils.etag(body, encoding)', function(){ @@ -14,8 +15,7 @@ describe('utils.etag(body, encoding)', function(){ }) it('should support buffer', function(){ - var buf = new Buffer('express!') - utils.etag(buf) + utils.etag(Buffer.from('express!')) .should.eql('"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') }) @@ -59,8 +59,7 @@ describe('utils.wetag(body, encoding)', function(){ }) it('should support buffer', function(){ - var buf = new Buffer('express!') - utils.wetag(buf) + utils.wetag(Buffer.from('express!')) .should.eql('W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') }) From 2df1ad26a58bf51228d7600df0d62ed17a90ff71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hrvoje=20=C5=A0imi=C4=87?= Date: Tue, 26 Sep 2017 16:00:53 +0200 Subject: [PATCH 057/479] Improve error messages when non-function provided as middleware closes #3426 --- History.md | 1 + lib/application.js | 2 +- lib/router/index.js | 4 ++-- lib/router/route.js | 4 ++-- test/Router.js | 30 +++++++++++++++++++++--------- test/app.use.js | 31 ++++++++++++++++++++++--------- 6 files changed, 49 insertions(+), 23 deletions(-) diff --git a/History.md b/History.md index 18f5e5da371..63a1bdf60b7 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * Improve error message when autoloading invalid view engine + * Improve error messages when non-function provided as middleware * Skip `Buffer` encoding when not generating ETag for small response * Use `safe-buffer` for improved Buffer API * deps: accepts@~1.3.4 diff --git a/lib/application.js b/lib/application.js index 8097d81a3b9..91f77d241e4 100644 --- a/lib/application.js +++ b/lib/application.js @@ -207,7 +207,7 @@ app.use = function use(fn) { var fns = flatten(slice.call(arguments, offset)); if (fns.length === 0) { - throw new TypeError('app.use() requires middleware functions'); + throw new TypeError('app.use() requires a middleware function') } // setup router diff --git a/lib/router/index.js b/lib/router/index.js index 51db4c28ff9..60727ed6d64 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -448,14 +448,14 @@ proto.use = function use(fn) { var callbacks = flatten(slice.call(arguments, offset)); if (callbacks.length === 0) { - throw new TypeError('Router.use() requires middleware functions'); + throw new TypeError('Router.use() requires a middleware function') } for (var i = 0; i < callbacks.length; i++) { var fn = callbacks[i]; if (typeof fn !== 'function') { - throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn)); + throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn)) } // add the middleware diff --git a/lib/router/route.js b/lib/router/route.js index ea82ed29df5..178df0d5160 100644 --- a/lib/router/route.js +++ b/lib/router/route.js @@ -175,7 +175,7 @@ Route.prototype.all = function all() { if (typeof handle !== 'function') { var type = toString.call(handle); - var msg = 'Route.all() requires callback functions but got a ' + type; + var msg = 'Route.all() requires a callback function but got a ' + type throw new TypeError(msg); } @@ -198,7 +198,7 @@ methods.forEach(function(method){ if (typeof handle !== 'function') { var type = toString.call(handle); - var msg = 'Route.' + method + '() requires callback functions but got a ' + type; + var msg = 'Route.' + method + '() requires a callback function but got a ' + type throw new Error(msg); } diff --git a/test/Router.js b/test/Router.js index 18153d29267..057ce443df4 100644 --- a/test/Router.js +++ b/test/Router.js @@ -368,17 +368,29 @@ describe('Router', function(){ }) describe('.use', function() { - it('should require arguments', function(){ - var router = new Router(); - router.use.bind(router).should.throw(/requires middleware function/) + it('should require middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/') }, /requires a middleware function/) }) - it('should not accept non-functions', function(){ - var router = new Router(); - router.use.bind(router, '/', 'hello').should.throw(/requires middleware function.*string/) - router.use.bind(router, '/', 5).should.throw(/requires middleware function.*number/) - router.use.bind(router, '/', null).should.throw(/requires middleware function.*Null/) - router.use.bind(router, '/', new Date()).should.throw(/requires middleware function.*Date/) + it('should reject string as middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/', 'foo') }, /requires a middleware function but got a string/) + }) + + it('should reject number as middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/', 42) }, /requires a middleware function but got a number/) + }) + + it('should reject null as middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/', null) }, /requires a middleware function but got a Null/) + }) + + it('should reject Date as middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/', new Date()) }, /requires a middleware function but got a Date/) }) it('should be called for any URL', function (done) { diff --git a/test/app.use.js b/test/app.use.js index b2031e4c56c..347937fbb3b 100644 --- a/test/app.use.js +++ b/test/app.use.js @@ -1,5 +1,6 @@ var after = require('after'); +var assert = require('assert') var express = require('..'); var request = require('supertest'); @@ -253,17 +254,29 @@ describe('app', function(){ }) describe('.use(path, middleware)', function(){ - it('should reject missing functions', function () { - var app = express(); - app.use.bind(app, '/').should.throw(/requires middleware function/); + it('should require middleware', function () { + var app = express() + assert.throws(function () { app.use('/') }, /requires a middleware function/) }) - it('should reject non-functions as middleware', function () { - var app = express(); - app.use.bind(app, '/', 'hi').should.throw(/requires middleware function.*string/); - app.use.bind(app, '/', 5).should.throw(/requires middleware function.*number/); - app.use.bind(app, '/', null).should.throw(/requires middleware function.*Null/); - app.use.bind(app, '/', new Date()).should.throw(/requires middleware function.*Date/); + it('should reject string as middleware', function () { + var app = express() + assert.throws(function () { app.use('/', 'foo') }, /requires a middleware function but got a string/) + }) + + it('should reject number as middleware', function () { + var app = express() + assert.throws(function () { app.use('/', 42) }, /requires a middleware function but got a number/) + }) + + it('should reject null as middleware', function () { + var app = express() + assert.throws(function () { app.use('/', null) }, /requires a middleware function but got a Null/) + }) + + it('should reject Date as middleware', function () { + var app = express() + assert.throws(function () { app.use('/', new Date()) }, /requires a middleware function but got a Date/) }) it('should strip path from req.url', function (done) { From 44591fee234dd83e05894c5b055703db1f68184c Mon Sep 17 00:00:00 2001 From: chainhelen Date: Thu, 28 Sep 2017 12:25:27 +0800 Subject: [PATCH 058/479] deps: vary@~1.1.2 closes #3434 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 63a1bdf60b7..04b68978330 100644 --- a/History.md +++ b/History.md @@ -26,6 +26,8 @@ unreleased - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 * deps: utils-merge@1.0.1 + * deps: vary@~1.1.2 + - perf: improve header token parsing speed * perf: re-use options object when generating ETags 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index dbbd7810042..0a476d67d90 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "statuses": "~1.3.1", "type-is": "~1.6.15", "utils-merge": "1.0.1", - "vary": "~1.1.1" + "vary": "~1.1.2" }, "devDependencies": { "after": "0.8.2", From 95fb5cc26848d4c2c57b0a7a74f088538d47d312 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 10:30:10 -0400 Subject: [PATCH 059/479] perf: remove dead .charset set in res.jsonp --- History.md | 1 + lib/response.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/History.md b/History.md index 04b68978330..c2d2595731a 100644 --- a/History.md +++ b/History.md @@ -29,6 +29,7 @@ unreleased * deps: vary@~1.1.2 - perf: improve header token parsing speed * perf: re-use options object when generating ETags + * perf: remove dead `.charset` set in `res.jsonp` 4.15.5 / 2017-09-24 =================== diff --git a/lib/response.js b/lib/response.js index 285daf4fb4b..9f61648c17b 100644 --- a/lib/response.js +++ b/lib/response.js @@ -314,7 +314,6 @@ res.jsonp = function jsonp(obj) { // jsonp if (typeof callback === 'string' && callback.length !== 0) { - this.charset = 'utf-8'; this.set('X-Content-Type-Options', 'nosniff'); this.set('Content-Type', 'text/javascript'); From a24fd0ca6cfd29329444fddf678bcdd1c08e56ae Mon Sep 17 00:00:00 2001 From: Aaron Clover Date: Thu, 20 Jul 2017 21:24:04 +1000 Subject: [PATCH 060/479] Add options to res.download closes #3327 closes #3370 --- lib/response.js | 34 ++++++++++++++++--- test/res.download.js | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/lib/response.js b/lib/response.js index 9f61648c17b..ae028ae81b6 100644 --- a/lib/response.js +++ b/lib/response.js @@ -515,19 +515,29 @@ res.sendfile = deprecate.function(res.sendfile, * when the data transfer is complete, or when an error has * ocurred. Be sure to check `res.headersSent` if you plan to respond. * - * This method uses `res.sendfile()`. + * Optionally providing an `options` object to use with `res.sendFile()`. + * This function will set the `Content-Disposition` header, overriding + * any `Content-Disposition` header passed as header options in order + * to set the attachment and filename. + * + * This method uses `res.sendFile()`. * * @public */ -res.download = function download(path, filename, callback) { +res.download = function download (path, filename, options, callback) { var done = callback; var name = filename; + var opts = options || null - // support function as second arg + // support function as second or third arg if (typeof filename === 'function') { done = filename; name = null; + opts = null + } else if (typeof options === 'function') { + done = options + opts = null } // set Content-Disposition when file is sent @@ -535,10 +545,26 @@ res.download = function download(path, filename, callback) { 'Content-Disposition': contentDisposition(name || path) }; + // merge user-provided headers + if (opts && opts.headers) { + var keys = Object.keys(opts.headers) + for (var i = 0; i < keys.length; i++) { + var key = keys[i] + if (key.toLowerCase() !== 'content-disposition') { + headers[key] = opts.headers[key] + } + } + } + + // merge user-provided options + opts = Object.create(opts) + opts.headers = headers + // Resolve the full path for sendFile var fullPath = resolve(path); - return this.sendFile(fullPath, { headers: headers }, done); + // send file + return this.sendFile(fullPath, opts, done) }; /** diff --git a/test/res.download.js b/test/res.download.js index fad56ee256a..30215bf6764 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -71,6 +71,86 @@ describe('res', function(){ }) }) + describe('.download(path, filename, options, fn)', function () { + it('should invoke the callback', function (done) { + var app = express() + var cb = after(2, done) + var options = {} + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', options, done) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(cb) + }) + + it('should allow options to res.sendFile()', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/.name', 'document', { + dotfiles: 'allow', + maxAge: '4h' + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Cache-Control', 'public, max-age=14400') + .expect('tobi') + .end(done) + }) + + describe('when options.headers contains Content-Disposition', function () { + it('should should be ignored', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', { + headers: { + 'Content-Type': 'text/x-custom', + 'Content-Disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(done) + }) + + it('should should be ignored case-insensitively', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', { + headers: { + 'content-type': 'text/x-custom', + 'content-disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(done) + }) + }) + }) + describe('on failure', function(){ it('should invoke the callback', function(done){ var app = express(); From 628438d8d890f3707b8eecf57aeff7d0da348e8e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 11:36:20 -0400 Subject: [PATCH 061/479] deps: update example dependencies --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a476d67d90..addbdcb7f30 100644 --- a/package.json +++ b/package.json @@ -59,18 +59,18 @@ }, "devDependencies": { "after": "0.8.2", - "body-parser": "1.18.1", + "body-parser": "1.18.2", "cookie-parser": "~1.4.3", - "cookie-session": "1.3.1", + "cookie-session": "1.3.2", "ejs": "2.5.7", "eslint": "2.13.1", - "express-session": "1.15.5", + "express-session": "1.15.6", "hbs": "4.0.1", "istanbul": "0.4.5", "marked": "0.3.6", - "method-override": "2.3.9", + "method-override": "2.3.10", "mocha": "3.5.3", - "morgan": "1.8.2", + "morgan": "1.9.0", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", "should": "13.1.0", From 715401478516c39ea9b2f855d4109d7d6e1131e0 Mon Sep 17 00:00:00 2001 From: Greg Guthe Date: Wed, 5 Apr 2017 17:42:09 -0400 Subject: [PATCH 062/479] Add "escape json" setting for res.json and res.jsonp closes #3268 closes #3269 --- History.md | 1 + lib/response.js | 36 +++++++++++++++++++++++++++++++----- test/res.json.js | 22 ++++++++++++++++++++++ test/res.jsonp.js | 22 ++++++++++++++++++++++ 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/History.md b/History.md index c2d2595731a..248b1c7a678 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Add `"json escape"` setting for `res.json` and `res.jsonp` * Improve error message when autoloading invalid view engine * Improve error messages when non-function provided as middleware * Skip `Buffer` encoding when not generating ETag for small response diff --git a/lib/response.js b/lib/response.js index ae028ae81b6..832044be9ae 100644 --- a/lib/response.js +++ b/lib/response.js @@ -254,9 +254,10 @@ res.json = function json(obj) { // settings var app = this.app; + var escape = app.get('json escape') var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); - var body = stringify(val, replacer, spaces); + var body = stringify(val, replacer, spaces, escape) // content-type if (!this.get('Content-Type')) { @@ -296,9 +297,10 @@ res.jsonp = function jsonp(obj) { // settings var app = this.app; + var escape = app.get('json escape') var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); - var body = stringify(val, replacer, spaces); + var body = stringify(val, replacer, spaces, escape) var callback = this.req.query[app.get('jsonp callback name')]; // content-type @@ -1098,14 +1100,38 @@ function sendfile(res, file, options, callback) { } /** - * Stringify JSON, like JSON.stringify, but v8 optimized. + * Stringify JSON, like JSON.stringify, but v8 optimized, with the + * ability to escape characters that can trigger HTML sniffing. + * + * @param {*} value + * @param {function} replaces + * @param {number} spaces + * @param {boolean} escape + * @returns {string} * @private */ -function stringify(value, replacer, spaces) { +function stringify (value, replacer, spaces, escape) { // v8 checks arguments.length for optimizing simple call // https://bugs.chromium.org/p/v8/issues/detail?id=4730 - return replacer || spaces + var json = replacer || spaces ? JSON.stringify(value, replacer, spaces) : JSON.stringify(value); + + if (escape) { + json = json.replace(/[<>&]/g, function (c) { + switch (c.charCodeAt(0)) { + case 0x3c: + return '\\u003c' + case 0x3e: + return '\\u003e' + case 0x26: + return '\\u0026' + default: + return c + } + }) + } + + return json } diff --git a/test/res.json.js b/test/res.json.js index 69f6723af54..1041376235c 100644 --- a/test/res.json.js +++ b/test/res.json.js @@ -102,6 +102,28 @@ describe('res', function(){ }) }) + describe('"json escape" setting', function () { + it('should be undefined by default', function () { + var app = express() + assert.strictEqual(app.get('json escape'), undefined) + }) + + it('should unicode escape HTML-sniffing characters', function (done) { + var app = express() + + app.enable('json escape') + + app.use(function (req, res) { + res.json({ '&': ' From bf4c3ee00f3b95a329fc1dd4dd0cd17c74178121 Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Sun, 20 Mar 2022 20:13:23 +0530 Subject: [PATCH 315/479] docs: fix incomplete JSDoc comment closes #4867 --- lib/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils.js b/lib/utils.js index 7797b068530..799a6a2b4ee 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -120,6 +120,7 @@ exports.contentDisposition = deprecate.function(contentDisposition, * also includes `.originalIndex` for stable sorting * * @param {String} str + * @param {Number} index * @return {Object} * @api private */ From 947b6b7d57939d1a3b33ce008765f9aba3eb6f70 Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Sun, 20 Mar 2022 21:08:08 +0530 Subject: [PATCH 316/479] lint: remove unnecessary continue statement in loop closes #4868 --- lib/router/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/router/index.js b/lib/router/index.js index 467d30458c2..791a600f86a 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -251,7 +251,6 @@ proto.handle = function handle(req, res, out) { // don't even bother matching route if (!has_method && method !== 'HEAD') { match = false; - continue; } } From eb4c930d5fb79e55c53b25d6e8a8a5cd15fc5554 Mon Sep 17 00:00:00 2001 From: Kris Kalavantavanich Date: Sun, 5 Dec 2021 15:14:40 +0700 Subject: [PATCH 317/479] build: support Node.js 15.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6e1b168ec1..8e60f419c62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: - Node.js 12.x - Node.js 13.x - Node.js 14.x + - Node.js 15.x include: - name: Node.js 0.10 @@ -90,6 +91,9 @@ jobs: - name: Node.js 14.x node-version: "14.19" + - name: Node.js 15.x + node-version: "15.14" + steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index db54a3fdb04..fc867adee7a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ environment: - nodejs_version: "12.22" - nodejs_version: "13.14" - nodejs_version: "14.19" + - nodejs_version: "15.14" cache: - node_modules install: From 8bf072039100cd264be920f06635fe77f083c751 Mon Sep 17 00:00:00 2001 From: Kris Kalavantavanich Date: Sun, 5 Dec 2021 15:15:26 +0700 Subject: [PATCH 318/479] build: support Node.js 16.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e60f419c62..7b153d1b436 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,7 @@ jobs: - Node.js 13.x - Node.js 14.x - Node.js 15.x + - Node.js 16.x include: - name: Node.js 0.10 @@ -94,6 +95,9 @@ jobs: - name: Node.js 15.x node-version: "15.14" + - name: Node.js 16.x + node-version: "16.14" + steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index fc867adee7a..2a2507b411b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,6 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.19" - nodejs_version: "15.14" + - nodejs_version: "16.14" cache: - node_modules install: From 87279c08aa46d178fe6f2e235d2345f17d6b5a37 Mon Sep 17 00:00:00 2001 From: "Tito D. Kesumo Siregar" Date: Thu, 20 May 2021 11:23:33 +0700 Subject: [PATCH 319/479] Support proper 205 responses using res.send closes #4592 closes #4596 --- History.md | 5 +++++ lib/response.js | 7 +++++++ test/res.send.js | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/History.md b/History.md index 9f3f876512d..fbe01269074 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Support proper 205 responses using `res.send` + 4.17.3 / 2022-02-16 =================== diff --git a/lib/response.js b/lib/response.js index ccf8d91b2c3..9cf3d52be52 100644 --- a/lib/response.js +++ b/lib/response.js @@ -213,6 +213,13 @@ res.send = function send(body) { chunk = ''; } + // alter headers for 205 + if (this.statusCode === 205) { + this.set('Content-Length', '0') + this.removeHeader('Transfer-Encoding') + chunk = '' + } + if (req.method === 'HEAD') { // skip body for HEAD this.end(); diff --git a/test/res.send.js b/test/res.send.js index 6ba55422882..c92568db6ad 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -283,6 +283,22 @@ describe('res', function(){ }) }) + describe('when .statusCode is 205', function () { + it('should strip Transfer-Encoding field and body, set Content-Length', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(205).set('Transfer-Encoding', 'chunked').send('foo') + }) + + request(app) + .get('/') + .expect(utils.shouldNotHaveHeader('Transfer-Encoding')) + .expect('Content-Length', '0') + .expect(205, '', done) + }) + }) + describe('when .statusCode is 304', function(){ it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){ var app = express(); From c17fe058613dc7dfb7779fbe68a9738a108fe408 Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Wed, 2 Feb 2022 19:13:54 -0600 Subject: [PATCH 320/479] Ignore Object.prototype values in settings through app.set/app.get closes #4802 closes #4803 --- History.md | 1 + lib/application.js | 19 ++++++++++++++++++- test/config.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index fbe01269074..97345bb411a 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` 4.17.3 / 2022-02-16 diff --git a/lib/application.js b/lib/application.js index e65ba588959..ebb30b51b3d 100644 --- a/lib/application.js +++ b/lib/application.js @@ -29,6 +29,13 @@ var flatten = require('array-flatten'); var merge = require('utils-merge'); var resolve = require('path').resolve; var setPrototypeOf = require('setprototypeof') + +/** + * Module variables. + * @private + */ + +var hasOwnProperty = Object.prototype.hasOwnProperty var slice = Array.prototype.slice; /** @@ -352,7 +359,17 @@ app.param = function param(name, fn) { app.set = function set(setting, val) { if (arguments.length === 1) { // app.get(setting) - return this.settings[setting]; + var settings = this.settings + + while (settings && settings !== Object.prototype) { + if (hasOwnProperty.call(settings, setting)) { + return settings[setting] + } + + settings = Object.getPrototypeOf(settings) + } + + return undefined } debug('set "%s" to %o', setting, val); diff --git a/test/config.js b/test/config.js index 8386a4471c3..b04367fdbf8 100644 --- a/test/config.js +++ b/test/config.js @@ -11,6 +11,12 @@ describe('config', function () { assert.equal(app.get('foo'), 'bar'); }) + it('should set prototype values', function () { + var app = express() + app.set('hasOwnProperty', 42) + assert.strictEqual(app.get('hasOwnProperty'), 42) + }) + it('should return the app', function () { var app = express(); assert.equal(app.set('foo', 'bar'), app); @@ -21,6 +27,17 @@ describe('config', function () { assert.equal(app.set('foo', undefined), app); }) + it('should return set value', function () { + var app = express() + app.set('foo', 'bar') + assert.strictEqual(app.set('foo'), 'bar') + }) + + it('should return undefined for prototype values', function () { + var app = express() + assert.strictEqual(app.set('hasOwnProperty'), undefined) + }) + describe('"etag"', function(){ it('should throw on bad value', function(){ var app = express(); @@ -51,6 +68,11 @@ describe('config', function () { assert.strictEqual(app.get('foo'), undefined); }) + it('should return undefined for prototype values', function () { + var app = express() + assert.strictEqual(app.get('hasOwnProperty'), undefined) + }) + it('should otherwise return the value', function(){ var app = express(); app.set('foo', 'bar'); @@ -125,6 +147,12 @@ describe('config', function () { assert.equal(app.enable('tobi'), app); assert.strictEqual(app.get('tobi'), true); }) + + it('should set prototype values', function () { + var app = express() + app.enable('hasOwnProperty') + assert.strictEqual(app.get('hasOwnProperty'), true) + }) }) describe('.disable()', function(){ @@ -133,6 +161,12 @@ describe('config', function () { assert.equal(app.disable('tobi'), app); assert.strictEqual(app.get('tobi'), false); }) + + it('should set prototype values', function () { + var app = express() + app.disable('hasOwnProperty') + assert.strictEqual(app.get('hasOwnProperty'), false) + }) }) describe('.enabled()', function(){ @@ -146,6 +180,11 @@ describe('config', function () { app.set('foo', 'bar'); assert.strictEqual(app.enabled('foo'), true); }) + + it('should default to false for prototype values', function () { + var app = express() + assert.strictEqual(app.enabled('hasOwnProperty'), false) + }) }) describe('.disabled()', function(){ @@ -159,5 +198,10 @@ describe('config', function () { app.set('foo', 'bar'); assert.strictEqual(app.disabled('foo'), false); }) + + it('should default to true for prototype values', function () { + var app = express() + assert.strictEqual(app.disabled('hasOwnProperty'), true) + }) }) }) From 4847d0efa123fae8f12a2c1b88f7e1a87a5f145a Mon Sep 17 00:00:00 2001 From: Jon Church Date: Sat, 21 Mar 2020 05:04:16 -0400 Subject: [PATCH 321/479] Deprecate string and non-integer arguments to res.status closes #4223 --- History.md | 1 + lib/response.js | 3 + test/res.status.js | 205 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 12 deletions(-) diff --git a/History.md b/History.md index 97345bb411a..2e549136a54 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` diff --git a/lib/response.js b/lib/response.js index 9cf3d52be52..7a9564d2625 100644 --- a/lib/response.js +++ b/lib/response.js @@ -64,6 +64,9 @@ var charsetRegExp = /;\s*charset\s*=/; */ res.status = function status(code) { + if ((typeof code === 'string' || Math.floor(code) !== code) && code > 99 && code < 1000) { + deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead') + } this.statusCode = code; return this; }; diff --git a/test/res.status.js b/test/res.status.js index e0abc73c4c0..1fe08344eaa 100644 --- a/test/res.status.js +++ b/test/res.status.js @@ -1,21 +1,202 @@ 'use strict' var express = require('../') - , request = require('supertest'); +var request = require('supertest') -describe('res', function(){ - describe('.status(code)', function(){ - it('should set the response .statusCode', function(done){ - var app = express(); +var isIoJs = process.release + ? process.release.name === 'io.js' + : ['v1.', 'v2.', 'v3.'].indexOf(process.version.slice(0, 3)) !== -1 - app.use(function(req, res){ - res.status(201).end('Created'); - }); +describe('res', function () { + describe('.status(code)', function () { + describe('when "code" is undefined', function () { + it('should raise error for invalid status code', function (done) { + var app = express() - request(app) - .get('/') - .expect('Created') - .expect(201, done); + app.use(function (req, res) { + res.status(undefined).end() + }) + + request(app) + .get('/') + .expect(500, /Invalid status code/, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) + }) + + describe('when "code" is null', function () { + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(null).end() + }) + + request(app) + .get('/') + .expect(500, /Invalid status code/, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) + }) + + describe('when "code" is 201', function () { + it('should set the response status code to 201', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(201).end() + }) + + request(app) + .get('/') + .expect(201, done) + }) + }) + + describe('when "code" is 302', function () { + it('should set the response status code to 302', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(302).end() + }) + + request(app) + .get('/') + .expect(302, done) + }) + }) + + describe('when "code" is 403', function () { + it('should set the response status code to 403', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(403).end() + }) + + request(app) + .get('/') + .expect(403, done) + }) + }) + + describe('when "code" is 501', function () { + it('should set the response status code to 501', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(501).end() + }) + + request(app) + .get('/') + .expect(501, done) + }) + }) + + describe('when "code" is "410"', function () { + it('should set the response status code to 410', function (done) { + var app = express() + + app.use(function (req, res) { + res.status('410').end() + }) + + request(app) + .get('/') + .expect(410, done) + }) + }) + + describe('when "code" is 410.1', function () { + it('should set the response status code to 410', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(410.1).end() + }) + + request(app) + .get('/') + .expect(410, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) + }) + + describe('when "code" is 1000', function () { + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(1000).end() + }) + + request(app) + .get('/') + .expect(500, /Invalid status code/, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) + }) + + describe('when "code" is 99', function () { + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(99).end() + }) + + request(app) + .get('/') + .expect(500, /Invalid status code/, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) + }) + + describe('when "code" is -401', function () { + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(-401).end() + }) + + request(app) + .get('/') + .expect(500, /Invalid status code/, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) }) }) }) From 0def9bb659557df1bd659411a1a6f2c5b8b9d893 Mon Sep 17 00:00:00 2001 From: Tommaso Tofacchi Date: Fri, 11 Mar 2022 10:02:43 +0100 Subject: [PATCH 322/479] Add "root" option to res.download fixes #4834 closes #4855 --- History.md | 1 + lib/response.js | 4 ++- test/res.download.js | 74 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 2e549136a54..d7a2e43c350 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Add "root" option to `res.download` * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` diff --git a/lib/response.js b/lib/response.js index 7a9564d2625..101311e0ebf 100644 --- a/lib/response.js +++ b/lib/response.js @@ -582,7 +582,9 @@ res.download = function download (path, filename, options, callback) { opts.headers = headers // Resolve the full path for sendFile - var fullPath = resolve(path); + var fullPath = !opts.root + ? resolve(path) + : path // send file return this.sendFile(fullPath, opts, done) diff --git a/test/res.download.js b/test/res.download.js index 1322b0a31f9..51380d4ba16 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -3,9 +3,12 @@ var after = require('after'); var Buffer = require('safe-buffer').Buffer var express = require('..'); +var path = require('path') var request = require('supertest'); var utils = require('./support/utils') +var FIXTURES_PATH = path.join(__dirname, 'fixtures') + describe('res', function(){ describe('.download(path)', function(){ it('should transfer as an attachment', function(done){ @@ -178,6 +181,77 @@ describe('res', function(){ .end(done) }) }) + + describe('with "root" option', function () { + it('should allow relative path', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('name.txt', 'document', { + root: FIXTURES_PATH + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="document"') + .expect(utils.shouldHaveBody(Buffer.from('tobi'))) + .end(done) + }) + + it('should allow up within root', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('fake/../name.txt', 'document', { + root: FIXTURES_PATH + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="document"') + .expect(utils.shouldHaveBody(Buffer.from('tobi'))) + .end(done) + }) + + it('should reject up outside root', function (done) { + var app = express() + + app.use(function (req, res) { + var p = '..' + path.sep + + path.relative(path.dirname(FIXTURES_PATH), path.join(FIXTURES_PATH, 'name.txt')) + + res.download(p, 'document', { + root: FIXTURES_PATH + }) + }) + + request(app) + .get('/') + .expect(403) + .expect(utils.shouldNotHaveHeader('Content-Disposition')) + .end(done) + }) + + it('should reject reading outside root', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('../name.txt', 'document', { + root: FIXTURES_PATH + }) + }) + + request(app) + .get('/') + .expect(403) + .expect(utils.shouldNotHaveHeader('Content-Disposition')) + .end(done) + }) + }) }) describe('on failure', function(){ From dd69eedd189eb55658ec435b821df13062cc5a8e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 01:43:45 -0400 Subject: [PATCH 323/479] deps: send@0.18.0 --- History.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index d7a2e43c350..c5242a99a47 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,14 @@ unreleased * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` + * deps: send@0.18.0 + - Fix emitted 416 error missing headers property + - Limit the headers removed for 304 response + - deps: depd@2.0.0 + - deps: destroy@1.2.0 + - deps: http-errors@2.0.0 + - deps: on-finished@2.4.1 + - deps: statuses@2.0.1 4.17.3 / 2022-02-16 =================== diff --git a/package.json b/package.json index 79921666294..14000074355 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "qs": "6.9.7", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", + "send": "0.18.0", "serve-static": "1.14.2", "setprototypeof": "1.2.0", "statuses": "~1.5.0", From c92420648e18f78d22db24e0ffec99155ec54a49 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 01:44:51 -0400 Subject: [PATCH 324/479] deps: serve-static@1.15.0 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c5242a99a47..9e4f0d3a8e9 100644 --- a/History.md +++ b/History.md @@ -13,6 +13,8 @@ unreleased - deps: http-errors@2.0.0 - deps: on-finished@2.4.1 - deps: statuses@2.0.1 + * deps: serve-static@1.15.0 + - deps: send@0.18.0 4.17.3 / 2022-02-16 =================== diff --git a/package.json b/package.json index 14000074355..c733bb2e507 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", - "serve-static": "1.14.2", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", "statuses": "~1.5.0", "type-is": "~1.6.18", From f739b162d9cc9fcfc1f514c2441c69f9fcb4364d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 01:46:32 -0400 Subject: [PATCH 325/479] deps: finalhandler@1.2.0 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 9e4f0d3a8e9..03b7ba9c7d9 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,10 @@ unreleased * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` + * deps: finalhandler@1.2.0 + - Remove set content headers that break response + - deps: on-finished@2.4.1 + - deps: statuses@2.0.1 * deps: send@0.18.0 - Fix emitted 416 error missing headers property - Limit the headers removed for 304 response diff --git a/package.json b/package.json index c733bb2e507..6de4bfff428 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From 03dc3671874b214f67dacaca90f39a1c389f822e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 18:13:42 -0400 Subject: [PATCH 326/479] Allow options without filename in res.download --- History.md | 1 + lib/response.js | 7 ++ test/res.download.js | 260 ++++++++++++++++++++++++++++++++----------- 3 files changed, 206 insertions(+), 62 deletions(-) diff --git a/History.md b/History.md index 03b7ba9c7d9..9b4bf4bce55 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * Add "root" option to `res.download` + * Allow `options` without `filename` in `res.download` * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` diff --git a/lib/response.js b/lib/response.js index 101311e0ebf..3713e6f9a97 100644 --- a/lib/response.js +++ b/lib/response.js @@ -561,6 +561,13 @@ res.download = function download (path, filename, options, callback) { opts = null } + // support optional filename, where options may be in it's place + if (typeof filename === 'object' && + (typeof options === 'function' || options === undefined)) { + name = null + opts = filename + } + // set Content-Disposition when file is sent var headers = { 'Content-Disposition': contentDisposition(name || path) diff --git a/test/res.download.js b/test/res.download.js index 51380d4ba16..91b074e8bf6 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -86,46 +86,12 @@ describe('res', function(){ }) }) - describe('.download(path, filename, fn)', function(){ - it('should invoke the callback', function(done){ - var app = express(); - var cb = after(2, done); - - app.use(function(req, res){ - res.download('test/fixtures/user.html', 'document', cb) - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="document"') - .expect(200, cb); - }) - }) - - describe('.download(path, filename, options, fn)', function () { - it('should invoke the callback', function (done) { - var app = express() - var cb = after(2, done) - var options = {} - - app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', options, cb) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(cb) - }) - + describe('.download(path, options)', function () { it('should allow options to res.sendFile()', function (done) { var app = express() app.use(function (req, res) { - res.download('test/fixtures/.name', 'document', { + res.download('test/fixtures/.name', { dotfiles: 'allow', maxAge: '4h' }) @@ -134,51 +100,124 @@ describe('res', function(){ request(app) .get('/') .expect(200) - .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Content-Disposition', 'attachment; filename=".name"') .expect('Cache-Control', 'public, max-age=14400') .expect(utils.shouldHaveBody(Buffer.from('tobi'))) .end(done) }) - describe('when options.headers contains Content-Disposition', function () { - it('should be ignored', function (done) { + describe('with "headers" option', function () { + it('should set headers on response', function (done) { var app = express() app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', { + res.download('test/fixtures/user.html', { headers: { - 'Content-Type': 'text/x-custom', - 'Content-Disposition': 'inline' + 'X-Foo': 'Bar', + 'X-Bar': 'Foo' } }) }) request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/x-custom') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(done) + .get('/') + .expect(200) + .expect('X-Foo', 'Bar') + .expect('X-Bar', 'Foo') + .end(done) }) - it('should be ignored case-insensitively', function (done) { + it('should use last header when duplicated', function (done) { var app = express() app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', { + res.download('test/fixtures/user.html', { headers: { - 'content-type': 'text/x-custom', - 'content-disposition': 'inline' + 'X-Foo': 'Bar', + 'x-foo': 'bar' } }) }) request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/x-custom') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(done) + .get('/') + .expect(200) + .expect('X-Foo', 'bar') + .end(done) + }) + + it('should override Content-Type', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', { + headers: { + 'Content-Type': 'text/x-custom' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .end(done) + }) + + it('should not set headers on 404', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/does-not-exist', { + headers: { + 'X-Foo': 'Bar' + } + }) + }) + + request(app) + .get('/') + .expect(404) + .expect(utils.shouldNotHaveHeader('X-Foo')) + .end(done) + }) + + describe('when headers contains Content-Disposition', function () { + it('should be ignored', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', { + headers: { + 'Content-Disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="user.html"') + .end(done) + }) + + it('should be ignored case-insensitively', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', { + headers: { + 'content-disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="user.html"') + .end(done) + }) }) }) @@ -187,7 +226,7 @@ describe('res', function(){ var app = express() app.use(function (req, res) { - res.download('name.txt', 'document', { + res.download('name.txt', { root: FIXTURES_PATH }) }) @@ -195,7 +234,7 @@ describe('res', function(){ request(app) .get('/') .expect(200) - .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Content-Disposition', 'attachment; filename="name.txt"') .expect(utils.shouldHaveBody(Buffer.from('tobi'))) .end(done) }) @@ -204,7 +243,7 @@ describe('res', function(){ var app = express() app.use(function (req, res) { - res.download('fake/../name.txt', 'document', { + res.download('fake/../name.txt', { root: FIXTURES_PATH }) }) @@ -212,7 +251,7 @@ describe('res', function(){ request(app) .get('/') .expect(200) - .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Content-Disposition', 'attachment; filename="name.txt"') .expect(utils.shouldHaveBody(Buffer.from('tobi'))) .end(done) }) @@ -224,7 +263,7 @@ describe('res', function(){ var p = '..' + path.sep + path.relative(path.dirname(FIXTURES_PATH), path.join(FIXTURES_PATH, 'name.txt')) - res.download(p, 'document', { + res.download(p, { root: FIXTURES_PATH }) }) @@ -240,7 +279,7 @@ describe('res', function(){ var app = express() app.use(function (req, res) { - res.download('../name.txt', 'document', { + res.download('../name.txt', { root: FIXTURES_PATH }) }) @@ -254,6 +293,103 @@ describe('res', function(){ }) }) + describe('.download(path, filename, fn)', function(){ + it('should invoke the callback', function(done){ + var app = express(); + var cb = after(2, done); + + app.use(function(req, res){ + res.download('test/fixtures/user.html', 'document', cb) + }); + + request(app) + .get('/') + .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Disposition', 'attachment; filename="document"') + .expect(200, cb); + }) + }) + + describe('.download(path, filename, options, fn)', function () { + it('should invoke the callback', function (done) { + var app = express() + var cb = after(2, done) + var options = {} + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', options, cb) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(cb) + }) + + it('should allow options to res.sendFile()', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/.name', 'document', { + dotfiles: 'allow', + maxAge: '4h' + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Cache-Control', 'public, max-age=14400') + .expect(utils.shouldHaveBody(Buffer.from('tobi'))) + .end(done) + }) + + describe('when options.headers contains Content-Disposition', function () { + it('should be ignored', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', { + headers: { + 'Content-Type': 'text/x-custom', + 'Content-Disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(done) + }) + + it('should be ignored case-insensitively', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', { + headers: { + 'content-type': 'text/x-custom', + 'content-disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(done) + }) + }) + }) + describe('on failure', function(){ it('should invoke the callback', function(done){ var app = express(); From 10b9b507b7d113d04965cccd8d170ee524e3d555 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 18:14:27 -0400 Subject: [PATCH 327/479] examples: use updated res.download in example --- examples/downloads/index.js | 5 +---- package.json | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/downloads/index.js b/examples/downloads/index.js index 62e7fa6e3eb..6b67e0c8862 100644 --- a/examples/downloads/index.js +++ b/examples/downloads/index.js @@ -6,7 +6,6 @@ var express = require('../../'); var path = require('path'); -var resolvePath = require('resolve-path') var app = module.exports = express(); @@ -25,9 +24,7 @@ app.get('/', function(req, res){ // /files/* is accessed via req.params[0] // but here we name it :file app.get('/files/:file(*)', function(req, res, next){ - var filePath = resolvePath(FILES_DIR, req.params.file) - - res.download(filePath, function (err) { + res.download(req.params.file, { root: FILES_DIR }, function (err) { if (!err) return; // file sent if (err.status !== 404) return next(err); // non-404 error // file for download not found diff --git a/package.json b/package.json index 6de4bfff428..8f8959a4c33 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,6 @@ "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", - "resolve-path": "1.4.0", "supertest": "6.2.2", "vhost": "~3.0.2" }, From 9482b82d0b9c498140561087d68f0409078a86ea Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Tue, 13 Mar 2018 08:14:06 +0200 Subject: [PATCH 328/479] Invoke default with same arguments as types in res.format closes #3587 --- History.md | 1 + lib/response.js | 9 ++++----- test/res.format.js | 29 ++++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/History.md b/History.md index 9b4bf4bce55..d1aa9b3b88d 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ unreleased * Allow `options` without `filename` in `res.download` * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` + * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` * deps: finalhandler@1.2.0 - Remove set content headers that break response diff --git a/lib/response.js b/lib/response.js index 3713e6f9a97..bfa7871434f 100644 --- a/lib/response.js +++ b/lib/response.js @@ -684,9 +684,8 @@ res.format = function(obj){ var req = this.req; var next = req.next; - var fn = obj.default; - if (fn) delete obj.default; - var keys = Object.keys(obj); + var keys = Object.keys(obj) + .filter(function (v) { return v !== 'default' }) var key = keys.length > 0 ? req.accepts(keys) @@ -697,8 +696,8 @@ res.format = function(obj){ if (key) { this.set('Content-Type', normalizeType(key).value); obj[key](req, this, next); - } else if (fn) { - fn(); + } else if (obj.default) { + obj.default(req, this, next) } else { var err = new Error('Not Acceptable'); err.status = err.statusCode = 406; diff --git a/test/res.format.js b/test/res.format.js index 24e18d95528..45243d17a1b 100644 --- a/test/res.format.js +++ b/test/res.format.js @@ -50,7 +50,12 @@ var app3 = express(); app3.use(function(req, res, next){ res.format({ text: function(){ res.send('hey') }, - default: function(){ res.send('default') } + default: function (a, b, c) { + assert(req === a) + assert(res === b) + assert(next === c) + res.send('default') + } }) }); @@ -118,6 +123,28 @@ describe('res', function(){ .set('Accept', '*/*') .expect('hey', done); }) + + it('should be able to invoke other formatter', function (done) { + var app = express() + + app.use(function (req, res, next) { + res.format({ + json: function () { res.send('json') }, + default: function () { + res.header('x-default', '1') + this.json() + } + }) + }) + + request(app) + .get('/') + .set('Accept', 'text/plain') + .expect(200) + .expect('x-default', '1') + .expect('json') + .end(done) + }) }) describe('in router', function(){ From 1cc816993832eba829a2f556f7c08e27e6371301 Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Wed, 5 Feb 2020 17:56:51 +0100 Subject: [PATCH 329/479] deps: depd@2.0.0 closes #4174 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index d1aa9b3b88d..0b54e1d27b2 100644 --- a/History.md +++ b/History.md @@ -7,6 +7,9 @@ unreleased * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` + * deps: depd@2.0.0 + - Replace internal `eval` usage with `Function` constructor + - Use instance methods on `process` to check for listeners * deps: finalhandler@1.2.0 - Remove set content headers that break response - deps: on-finished@2.4.1 diff --git a/package.json b/package.json index 8f8959a4c33..d9fbe976986 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "cookie": "0.4.2", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", From 5855339455a7f60774bef4166829e742a5056fa8 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Thu, 18 Apr 2019 09:12:33 -0400 Subject: [PATCH 330/479] Fix behavior of null/undefined as "maxAge" in res.cookie fixes #3935 closes #3936 --- History.md | 1 + lib/response.js | 10 +++++++--- test/res.cookie.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 0b54e1d27b2..8d9d39b2b80 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ unreleased * Add "root" option to `res.download` * Allow `options` without `filename` in `res.download` * Deprecate string and non-integer arguments to `res.status` + * Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` diff --git a/lib/response.js b/lib/response.js index bfa7871434f..eeeee1c806b 100644 --- a/lib/response.js +++ b/lib/response.js @@ -868,9 +868,13 @@ res.cookie = function (name, value, options) { val = 's:' + sign(val, secret); } - if ('maxAge' in opts) { - opts.expires = new Date(Date.now() + opts.maxAge); - opts.maxAge /= 1000; + if (opts.maxAge != null) { + var maxAge = opts.maxAge - 0 + + if (!isNaN(maxAge)) { + opts.expires = new Date(Date.now() + maxAge) + opts.maxAge = Math.floor(maxAge / 1000) + } } if (opts.path == null) { diff --git a/test/res.cookie.js b/test/res.cookie.js index d10e48646b6..e3a921301f4 100644 --- a/test/res.cookie.js +++ b/test/res.cookie.js @@ -111,6 +111,36 @@ describe('res', function(){ .expect(200, optionsCopy, done) }) + it('should not throw on null', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { maxAge: null }) + res.end() + }) + + request(app) + .get('/') + .expect(200) + .expect('Set-Cookie', 'name=tobi; Path=/') + .end(done) + }) + + it('should not throw on undefined', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { maxAge: undefined }) + res.end() + }) + + request(app) + .get('/') + .expect(200) + .expect('Set-Cookie', 'name=tobi; Path=/') + .end(done) + }) + it('should throw an error with invalid maxAge', function (done) { var app = express() From a10770286e7420c5a56ed3cc0b6add2c028ae56e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 27 Mar 2022 23:41:31 -0400 Subject: [PATCH 331/479] Use http-errors for res.format error --- History.md | 1 + lib/response.js | 8 ++++---- package.json | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 8d9d39b2b80..2e542333d91 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,7 @@ unreleased * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` + * Use `http-errors` for `res.format` error * deps: depd@2.0.0 - Replace internal `eval` usage with `Function` constructor - Use instance methods on `process` to check for listeners diff --git a/lib/response.js b/lib/response.js index eeeee1c806b..d9b8db1c201 100644 --- a/lib/response.js +++ b/lib/response.js @@ -14,6 +14,7 @@ var Buffer = require('safe-buffer').Buffer var contentDisposition = require('content-disposition'); +var createError = require('http-errors') var deprecate = require('depd')('express'); var encodeUrl = require('encodeurl'); var escapeHtml = require('escape-html'); @@ -699,10 +700,9 @@ res.format = function(obj){ } else if (obj.default) { obj.default(req, this, next) } else { - var err = new Error('Not Acceptable'); - err.status = err.statusCode = 406; - err.types = normalizeTypes(keys).map(function(o){ return o.value }); - next(err); + next(createError(406, { + types: normalizeTypes(keys).map(function (o) { return o.value }) + })) } return this; diff --git a/package.json b/package.json index d9fbe976986..71c110e5f23 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "etag": "~1.8.1", "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", From 32c558d414b4ac0f5bd70fa6a0f39a5558a7b016 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 2 Apr 2022 21:51:31 -0400 Subject: [PATCH 332/479] deps: body-parser@1.20.0 --- History.md | 10 ++ package.json | 2 +- test/express.json.js | 339 +++++++++++++++++++++++++------------ test/express.raw.js | 203 ++++++++++++++++++++-- test/express.text.js | 221 ++++++++++++++++++++---- test/express.urlencoded.js | 263 ++++++++++++++++++++-------- 6 files changed, 817 insertions(+), 221 deletions(-) diff --git a/History.md b/History.md index 2e542333d91..cba82afcbd2 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,16 @@ unreleased * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` * Use `http-errors` for `res.format` error + * deps: body-parser@1.20.0 + - Fix error message for json parse whitespace in `strict` + - Fix internal error when inflated body exceeds limit + - Prevent loss of async hooks context + - Prevent hanging when request already read + - deps: depd@2.0.0 + - deps: http-errors@2.0.0 + - deps: on-finished@2.4.1 + - deps: qs@6.10.3 + - deps: raw-body@2.5.1 * deps: depd@2.0.0 - Replace internal `eval` usage with `Function` constructor - Use instance methods on `process` to check for listeners diff --git a/package.json b/package.json index 71c110e5f23..ce6604bd7dd 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.2", + "body-parser": "1.20.0", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.4.2", diff --git a/test/express.json.js b/test/express.json.js index 53a39565a9b..a8cfebc41e2 100644 --- a/test/express.json.js +++ b/test/express.json.js @@ -1,10 +1,15 @@ 'use strict' var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..') var request = require('supertest') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('express.json()', function () { it('should parse JSON', function (done) { request(createApp()) @@ -38,6 +43,14 @@ describe('express.json()', function () { .expect(200, '{}', done) }) + it('should 400 when only whitespace', function (done) { + request(createApp()) + .post('/') + .set('Content-Type', 'application/json') + .send(' \n') + .expect(400, '[entity.parse.failed] ' + parseError(' '), done) + }) + it('should 400 when invalid content-length', function (done) { var app = express() @@ -59,6 +72,32 @@ describe('express.json()', function () { .expect(400, /content length/, done) }) + it('should 500 if stream not readable', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.on('end', next) + req.resume() + }) + + app.use(express.json()) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(500, '[stream.not.readable] stream is not readable', done) + }) + it('should handle duplicated middleware', function (done) { var app = express() @@ -86,7 +125,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('{:') - .expect(400, parseError('{:'), done) + .expect(400, '[entity.parse.failed] ' + parseError('{:'), done) }) it('should 400 for incomplete', function (done) { @@ -94,16 +133,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('{"user"') - .expect(400, parseError('{"user"'), done) - }) - - it('should error with type = "entity.parse.failed"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .set('X-Error-Property', 'type') - .send(' {"user"') - .expect(400, 'entity.parse.failed', done) + .expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done) }) it('should include original body on error object', function (done) { @@ -124,24 +154,13 @@ describe('express.json()', function () { .set('Content-Type', 'application/json') .set('Content-Length', '1034') .send(JSON.stringify({ str: buf.toString() })) - .expect(413, done) - }) - - it('should error with type = "entity.too.large"', function (done) { - var buf = Buffer.alloc(1024, '.') - request(createApp({ limit: '1kb' })) - .post('/') - .set('Content-Type', 'application/json') - .set('Content-Length', '1034') - .set('X-Error-Property', 'type') - .send(JSON.stringify({ str: buf.toString() })) - .expect(413, 'entity.too.large', done) + .expect(413, '[entity.too.large] request entity too large', done) }) it('should 413 when over limit with chunked encoding', function (done) { + var app = createApp({ limit: '1kb' }) var buf = Buffer.alloc(1024, '.') - var server = createApp({ limit: '1kb' }) - var test = request(server).post('/') + var test = request(app).post('/') test.set('Content-Type', 'application/json') test.set('Transfer-Encoding', 'chunked') test.write('{"str":') @@ -149,6 +168,15 @@ describe('express.json()', function () { test.expect(413, done) }) + it('should 413 when inflated body over limit', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex')) + test.expect(413, done) + }) + it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1024, '.') request(createApp({ limit: 1024 })) @@ -161,11 +189,11 @@ describe('express.json()', function () { it('should not change when options altered', function (done) { var buf = Buffer.alloc(1024, '.') var options = { limit: '1kb' } - var server = createApp(options) + var app = createApp(options) options.limit = '100kb' - request(server) + request(app) .post('/') .set('Content-Type', 'application/json') .send(JSON.stringify({ str: buf.toString() })) @@ -174,14 +202,23 @@ describe('express.json()', function () { it('should not hang response', function (done) { var buf = Buffer.alloc(10240, '.') - var server = createApp({ limit: '8kb' }) - var test = request(server).post('/') + var app = createApp({ limit: '8kb' }) + var test = request(app).post('/') test.set('Content-Type', 'application/json') test.write(buf) test.write(buf) test.write(buf) test.expect(413, done) }) + + it('should not error when inflating', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex')) + test.expect(413, done) + }) }) describe('with inflate option', function () { @@ -195,7 +232,7 @@ describe('express.json()', function () { test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(415, 'content encoding unsupported', done) + test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) @@ -225,7 +262,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('true') - .expect(400, parseError('#rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace('#', 't'), done) }) }) @@ -253,7 +290,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('true') - .expect(400, parseError('#rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace('#', 't'), done) }) it('should not parse primitives with leading whitespaces', function (done) { @@ -261,7 +298,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send(' true') - .expect(400, parseError(' #rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace('#', 't'), done) }) it('should allow leading whitespaces in JSON', function (done) { @@ -272,15 +309,6 @@ describe('express.json()', function () { .expect(200, '{"user":"tobi"}', done) }) - it('should error with type = "entity.parse.failed"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .set('X-Error-Property', 'type') - .send('true') - .expect(400, 'entity.parse.failed', done) - }) - it('should include correct message in stack trace', function (done) { request(this.app) .post('/') @@ -397,65 +425,59 @@ describe('express.json()', function () { }) it('should error from verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('["tobi"]') - .expect(403, 'no arrays', done) - }) - - it('should error with type = "entity.verify.failed"', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) request(app) .post('/') .set('Content-Type', 'application/json') - .set('X-Error-Property', 'type') .send('["tobi"]') - .expect(403, 'entity.verify.failed', done) + .expect(403, '[entity.verify.failed] no arrays', done) }) it('should allow custom codes', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x5b) return - var err = new Error('no arrays') - err.status = 400 - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x5b) return + var err = new Error('no arrays') + err.status = 400 + throw err + } + }) request(app) .post('/') .set('Content-Type', 'application/json') .send('["tobi"]') - .expect(400, 'no arrays', done) + .expect(400, '[entity.verify.failed] no arrays', done) }) it('should allow custom type', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x5b) return - var err = new Error('no arrays') - err.type = 'foo.bar' - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x5b) return + var err = new Error('no arrays') + err.type = 'foo.bar' + throw err + } + }) request(app) .post('/') .set('Content-Type', 'application/json') - .set('X-Error-Property', 'type') .send('["tobi"]') - .expect(403, 'foo.bar', done) + .expect(403, '[foo.bar] no arrays', done) }) it('should include original body on error object', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) request(app) .post('/') @@ -466,9 +488,11 @@ describe('express.json()', function () { }) it('should allow pass-through', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) request(app) .post('/') @@ -478,9 +502,11 @@ describe('express.json()', function () { }) it('should work with different charsets', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/json; charset=utf-16') @@ -489,14 +515,120 @@ describe('express.json()', function () { }) it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } }) + var app = createApp({ + verify: function (req, res, buf) { + throw new Error('unexpected verify call') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/json; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) - test.expect(415, 'unsupported charset "X-BOGUS"', done) + test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) + }) + }) + + describeAsyncHooks('async local storage', function () { + before(function () { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(express.json()) + + app.use(function (req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + next() + }) + + app.use(function (err, req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + this.app = app + }) + + it('should presist store', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{"user":"tobi"}') + .end(done) + }) + + it('should presist store when unmatched content-type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/fizzbuzz') + .send('buzz') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{}') + .end(done) + }) + + it('should presist store when inflated', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) + test.expect(200) + test.expect('x-store-foo', 'bar') + test.expect('{"name":"论"}') + test.end(done) + }) + + it('should presist store when inflate error', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) + test.expect(400) + test.expect('x-store-foo', 'bar') + test.end(done) + }) + + it('should presist store when parse error', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":') + .expect(400) + .expect('x-store-foo', 'bar') + .end(done) + }) + + it('should presist store when limit exceeded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}') + .expect(413) + .expect('x-store-foo', 'bar') + .end(done) }) }) @@ -538,15 +670,7 @@ describe('express.json()', function () { var test = request(this.app).post('/') test.set('Content-Type', 'application/json; charset=koi8-r') test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex')) - test.expect(415, 'unsupported charset "KOI8-R"', done) - }) - - it('should error with type = "charset.unsupported"', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/json; charset=koi8-r') - test.set('X-Error-Property', 'type') - test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex')) - test.expect(415, 'charset.unsupported', done) + test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done) }) }) @@ -599,16 +723,7 @@ describe('express.json()', function () { test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/json') test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'unsupported content encoding "nulls"', done) - }) - - it('should error with type = "encoding.unsupported"', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'nulls') - test.set('Content-Type', 'application/json') - test.set('X-Error-Property', 'type') - test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'encoding.unsupported', done) + test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) it('should 400 on malformed encoding', function (done) { @@ -639,7 +754,9 @@ function createApp (options) { app.use(function (err, req, res, next) { res.status(err.status || 500) - res.send(String(err[req.headers['x-error-property'] || 'message'])) + res.send(String(req.headers['x-error-property'] + ? err[req.headers['x-error-property']] + : ('[' + err.type + '] ' + err.message))) }) app.post('/', function (req, res) { @@ -663,3 +780,11 @@ function shouldContainInBody (str) { 'expected \'' + res.text + '\' to contain \'' + str + '\'') } } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} diff --git a/test/express.raw.js b/test/express.raw.js index cbd0736e7cb..4aa62bb85bc 100644 --- a/test/express.raw.js +++ b/test/express.raw.js @@ -1,10 +1,15 @@ 'use strict' var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..') var request = require('supertest') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('express.raw()', function () { before(function () { this.app = createApp() @@ -60,6 +65,36 @@ describe('express.raw()', function () { .expect(200, { buf: '' }, done) }) + it('should 500 if stream not readable', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.on('end', next) + req.resume() + }) + + app.use(express.raw()) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + if (Buffer.isBuffer(req.body)) { + res.json({ buf: req.body.toString('hex') }) + } else { + res.json(req.body) + } + }) + + request(app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .send('the user is tobi') + .expect(500, '[stream.not.readable] stream is not readable', done) + }) + it('should handle duplicated middleware', function (done) { var app = express() @@ -102,6 +137,15 @@ describe('express.raw()', function () { test.expect(413, done) }) + it('should 413 when inflated body over limit', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex')) + test.expect(413, done) + }) + it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1028, '.') var app = createApp({ limit: 1024 }) @@ -134,6 +178,15 @@ describe('express.raw()', function () { test.write(buf) test.expect(413, done) }) + + it('should not error when inflating', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex')) + test.expect(413, done) + }) }) describe('with inflate option', function () { @@ -147,7 +200,7 @@ describe('express.raw()', function () { test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(415, 'content encoding unsupported', done) + test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) @@ -263,34 +316,40 @@ describe('express.raw()', function () { }) it('should error from verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x00) throw new Error('no leading null') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x00) throw new Error('no leading null') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) - test.expect(403, 'no leading null', done) + test.expect(403, '[entity.verify.failed] no leading null', done) }) it('should allow custom codes', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x00) return - var err = new Error('no leading null') - err.status = 400 - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x00) return + var err = new Error('no leading null') + err.status = 400 + throw err + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) - test.expect(400, 'no leading null', done) + test.expect(400, '[entity.verify.failed] no leading null', done) }) it('should allow pass-through', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x00) throw new Error('no leading null') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x00) throw new Error('no leading null') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/octet-stream') @@ -299,6 +358,104 @@ describe('express.raw()', function () { }) }) + describeAsyncHooks('async local storage', function () { + before(function () { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(express.raw()) + + app.use(function (req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + next() + }) + + app.use(function (err, req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + if (Buffer.isBuffer(req.body)) { + res.json({ buf: req.body.toString('hex') }) + } else { + res.json(req.body) + } + }) + + this.app = app + }) + + it('should presist store', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .send('the user is tobi') + .expect(200) + .expect('x-store-foo', 'bar') + .expect({ buf: '746865207573657220697320746f6269' }) + .end(done) + }) + + it('should presist store when unmatched content-type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/fizzbuzz') + .send('buzz') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{}') + .end(done) + }) + + it('should presist store when inflated', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) + test.expect(200) + test.expect('x-store-foo', 'bar') + test.expect({ buf: '6e616d653de8aeba' }) + test.end(done) + }) + + it('should presist store when inflate error', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex')) + test.expect(400) + test.expect('x-store-foo', 'bar') + test.end(done) + }) + + it('should presist store when limit exceeded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .send('the user is ' + Buffer.alloc(1024 * 100, '.').toString()) + .expect(413) + .expect('x-store-foo', 'bar') + .end(done) + }) + }) + describe('charset', function () { before(function () { this.app = createApp() @@ -356,12 +513,12 @@ describe('express.raw()', function () { test.expect(200, { buf: '6e616d653de8aeba' }, done) }) - it('should fail on unknown encoding', function (done) { + it('should 415 on unknown encoding', function (done) { var test = request(this.app).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'unsupported content encoding "nulls"', done) + test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) @@ -373,7 +530,9 @@ function createApp (options) { app.use(function (err, req, res, next) { res.status(err.status || 500) - res.send(String(err[req.headers['x-error-property'] || 'message'])) + res.send(String(req.headers['x-error-property'] + ? err[req.headers['x-error-property']] + : ('[' + err.type + '] ' + err.message))) }) app.post('/', function (req, res) { @@ -386,3 +545,11 @@ function createApp (options) { return app } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} diff --git a/test/express.text.js b/test/express.text.js index ebc12cd1098..cb7750a525c 100644 --- a/test/express.text.js +++ b/test/express.text.js @@ -1,10 +1,15 @@ 'use strict' var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..') var request = require('supertest') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('express.text()', function () { before(function () { this.app = createApp() @@ -56,6 +61,32 @@ describe('express.text()', function () { .expect(200, '""', done) }) + it('should 500 if stream not readable', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.on('end', next) + req.resume() + }) + + app.use(express.text()) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(500, '[stream.not.readable] stream is not readable', done) + }) + it('should handle duplicated middleware', function (done) { var app = express() @@ -75,16 +106,16 @@ describe('express.text()', function () { describe('with defaultCharset option', function () { it('should change default charset', function (done) { - var app = createApp({ defaultCharset: 'koi8-r' }) - var test = request(app).post('/') + var server = createApp({ defaultCharset: 'koi8-r' }) + var test = request(server).post('/') test.set('Content-Type', 'text/plain') test.write(Buffer.from('6e616d6520697320cec5d4', 'hex')) test.expect(200, '"name is нет"', done) }) it('should honor content-type charset', function (done) { - var app = createApp({ defaultCharset: 'koi8-r' }) - var test = request(app).post('/') + var server = createApp({ defaultCharset: 'koi8-r' }) + var test = request(server).post('/') test.set('Content-Type', 'text/plain; charset=utf-8') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, '"name is 论"', done) @@ -103,8 +134,8 @@ describe('express.text()', function () { }) it('should 413 when over limit with chunked encoding', function (done) { - var buf = Buffer.alloc(1028, '.') var app = createApp({ limit: '1kb' }) + var buf = Buffer.alloc(1028, '.') var test = request(app).post('/') test.set('Content-Type', 'text/plain') test.set('Transfer-Encoding', 'chunked') @@ -112,6 +143,15 @@ describe('express.text()', function () { test.expect(413, done) }) + it('should 413 when inflated body over limit', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex')) + test.expect(413, done) + }) + it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1028, '.') request(createApp({ limit: 1024 })) @@ -136,8 +176,8 @@ describe('express.text()', function () { }) it('should not hang response', function (done) { - var buf = Buffer.alloc(10240, '.') var app = createApp({ limit: '8kb' }) + var buf = Buffer.alloc(10240, '.') var test = request(app).post('/') test.set('Content-Type', 'text/plain') test.write(buf) @@ -145,6 +185,17 @@ describe('express.text()', function () { test.write(buf) test.expect(413, done) }) + + it('should not error when inflating', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a1470404', 'hex')) + setTimeout(function () { + test.expect(413, done) + }, 100) + }) }) describe('with inflate option', function () { @@ -158,7 +209,7 @@ describe('express.text()', function () { test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) - test.expect(415, 'content encoding unsupported', done) + test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) @@ -278,36 +329,42 @@ describe('express.text()', function () { }) it('should error from verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x20) throw new Error('no leading space') + } + }) request(app) .post('/') .set('Content-Type', 'text/plain') .send(' user is tobi') - .expect(403, 'no leading space', done) + .expect(403, '[entity.verify.failed] no leading space', done) }) it('should allow custom codes', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.status = 400 - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x20) return + var err = new Error('no leading space') + err.status = 400 + throw err + } + }) request(app) .post('/') .set('Content-Type', 'text/plain') .send(' user is tobi') - .expect(400, 'no leading space', done) + .expect(400, '[entity.verify.failed] no leading space', done) }) it('should allow pass-through', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x20) throw new Error('no leading space') + } + }) request(app) .post('/') @@ -317,14 +374,110 @@ describe('express.text()', function () { }) it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } }) + var app = createApp({ + verify: function (req, res, buf) { + throw new Error('unexpected verify call') + } + }) var test = request(app).post('/') test.set('Content-Type', 'text/plain; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) - test.expect(415, 'unsupported charset "X-BOGUS"', done) + test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) + }) + }) + + describeAsyncHooks('async local storage', function () { + before(function () { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(express.text()) + + app.use(function (req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + next() + }) + + app.use(function (err, req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + this.app = app + }) + + it('should presist store', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('"user is tobi"') + .end(done) + }) + + it('should presist store when unmatched content-type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/fizzbuzz') + .send('buzz') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{}') + .end(done) + }) + + it('should presist store when inflated', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) + test.expect(200) + test.expect('x-store-foo', 'bar') + test.expect('"name is 论"') + test.end(done) + }) + + it('should presist store when inflate error', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000', 'hex')) + test.expect(400) + test.expect('x-store-foo', 'bar') + test.end(done) + }) + + it('should presist store when limit exceeded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is ' + Buffer.alloc(1024 * 100, '.').toString()) + .expect(413) + .expect('x-store-foo', 'bar') + .end(done) }) }) @@ -366,7 +519,7 @@ describe('express.text()', function () { var test = request(this.app).post('/') test.set('Content-Type', 'text/plain; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) - test.expect(415, 'unsupported charset "X-BOGUS"', done) + test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) }) }) @@ -414,12 +567,12 @@ describe('express.text()', function () { test.expect(200, '"name is 论"', done) }) - it('should fail on unknown encoding', function (done) { + it('should 415 on unknown encoding', function (done) { var test = request(this.app).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'text/plain') test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'unsupported content encoding "nulls"', done) + test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) @@ -431,7 +584,9 @@ function createApp (options) { app.use(function (err, req, res, next) { res.status(err.status || 500) - res.send(err.message) + res.send(String(req.headers['x-error-property'] + ? err[req.headers['x-error-property']] + : ('[' + err.type + '] ' + err.message))) }) app.post('/', function (req, res) { @@ -440,3 +595,11 @@ function createApp (options) { return app } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} diff --git a/test/express.urlencoded.js b/test/express.urlencoded.js index 340eb74316c..e07432c86c3 100644 --- a/test/express.urlencoded.js +++ b/test/express.urlencoded.js @@ -1,10 +1,15 @@ 'use strict' var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..') var request = require('supertest') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('express.urlencoded()', function () { before(function () { this.app = createApp() @@ -57,6 +62,32 @@ describe('express.urlencoded()', function () { .expect(200, '{}', done) }) + it('should 500 if stream not readable', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.on('end', next) + req.resume() + }) + + app.use(express.urlencoded()) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(500, '[stream.not.readable] stream is not readable', done) + }) + it('should handle duplicated middleware', function (done) { var app = express() @@ -217,7 +248,7 @@ describe('express.urlencoded()', function () { test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(415, 'content encoding unsupported', done) + test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) @@ -248,8 +279,8 @@ describe('express.urlencoded()', function () { }) it('should 413 when over limit with chunked encoding', function (done) { - var buf = Buffer.alloc(1024, '.') var app = createApp({ limit: '1kb' }) + var buf = Buffer.alloc(1024, '.') var test = request(app).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded') test.set('Transfer-Encoding', 'chunked') @@ -258,6 +289,15 @@ describe('express.urlencoded()', function () { test.expect(413, done) }) + it('should 413 when inflated body over limit', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex')) + test.expect(413, done) + }) + it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1024, '.') request(createApp({ limit: 1024 })) @@ -282,8 +322,8 @@ describe('express.urlencoded()', function () { }) it('should not hang response', function (done) { - var buf = Buffer.alloc(10240, '.') var app = createApp({ limit: '8kb' }) + var buf = Buffer.alloc(10240, '.') var test = request(app).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(buf) @@ -291,6 +331,15 @@ describe('express.urlencoded()', function () { test.write(buf) test.expect(413, done) }) + + it('should not error when inflating', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex')) + test.expect(413, done) + }) }) describe('with parameterLimit option', function () { @@ -310,16 +359,7 @@ describe('express.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(11)) - .expect(413, /too many parameters/, done) - }) - - it('should error with type = "parameters.too.many"', function (done) { - request(createApp({ extended: false, parameterLimit: 10 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .set('X-Error-Property', 'type') - .send(createManyParams(11)) - .expect(413, 'parameters.too.many', done) + .expect(413, '[parameters.too.many] too many parameters', done) }) it('should work when at the limit', function (done) { @@ -374,16 +414,7 @@ describe('express.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(11)) - .expect(413, /too many parameters/, done) - }) - - it('should error with type = "parameters.too.many"', function (done) { - request(createApp({ extended: true, parameterLimit: 10 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .set('X-Error-Property', 'type') - .send(createManyParams(11)) - .expect(413, 'parameters.too.many', done) + .expect(413, '[parameters.too.many] too many parameters', done) }) it('should work when at the limit', function (done) { @@ -526,65 +557,59 @@ describe('express.urlencoded()', function () { }) it('should error from verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(' user=tobi') - .expect(403, 'no leading space', done) - }) - - it('should error with type = "entity.verify.failed"', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x20) throw new Error('no leading space') + } + }) request(app) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') - .set('X-Error-Property', 'type') .send(' user=tobi') - .expect(403, 'entity.verify.failed', done) + .expect(403, '[entity.verify.failed] no leading space', done) }) it('should allow custom codes', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.status = 400 - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x20) return + var err = new Error('no leading space') + err.status = 400 + throw err + } + }) request(app) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(' user=tobi') - .expect(400, 'no leading space', done) + .expect(400, '[entity.verify.failed] no leading space', done) }) it('should allow custom type', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.type = 'foo.bar' - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x20) return + var err = new Error('no leading space') + err.type = 'foo.bar' + throw err + } + }) request(app) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') - .set('X-Error-Property', 'type') .send(' user=tobi') - .expect(403, 'foo.bar', done) + .expect(403, '[foo.bar] no leading space', done) }) it('should allow pass-through', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) request(app) .post('/') @@ -594,14 +619,110 @@ describe('express.urlencoded()', function () { }) it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } }) + var app = createApp({ + verify: function (req, res, buf) { + throw new Error('unexpected verify call') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) - test.expect(415, 'unsupported charset "X-BOGUS"', done) + test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) + }) + }) + + describeAsyncHooks('async local storage', function () { + before(function () { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(express.urlencoded()) + + app.use(function (req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + next() + }) + + app.use(function (err, req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + this.app = app + }) + + it('should presist store', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{"user":"tobi"}') + .end(done) + }) + + it('should presist store when unmatched content-type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/fizzbuzz') + .send('buzz') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{}') + .end(done) + }) + + it('should presist store when inflated', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) + test.expect(200) + test.expect('x-store-foo', 'bar') + test.expect('{"name":"论"}') + test.end(done) + }) + + it('should presist store when inflate error', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex')) + test.expect(400) + test.expect('x-store-foo', 'bar') + test.end(done) + }) + + it('should presist store when limit exceeded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=' + Buffer.alloc(1024 * 100, '.').toString()) + .expect(413) + .expect('x-store-foo', 'bar') + .end(done) }) }) @@ -636,7 +757,7 @@ describe('express.urlencoded()', function () { var test = request(this.app).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r') test.write(Buffer.from('6e616d653dcec5d4', 'hex')) - test.expect(415, 'unsupported charset "KOI8-R"', done) + test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done) }) }) @@ -684,12 +805,12 @@ describe('express.urlencoded()', function () { test.expect(200, '{"name":"论"}', done) }) - it('should fail on unknown encoding', function (done) { + it('should 415 on unknown encoding', function (done) { var test = request(this.app).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'unsupported content encoding "nulls"', done) + test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) @@ -718,7 +839,9 @@ function createApp (options) { app.use(function (err, req, res, next) { res.status(err.status || 500) - res.send(String(err[req.headers['x-error-property'] || 'message'])) + res.send(String(req.headers['x-error-property'] + ? err[req.headers['x-error-property']] + : ('[' + err.type + '] ' + err.message))) }) app.post('/', function (req, res) { @@ -733,3 +856,11 @@ function expectKeyCount (count) { assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count) } } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} From 1df75763e315bd0582669238cd14baadec1d6db5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 2 Apr 2022 21:56:41 -0400 Subject: [PATCH 333/479] deps: qs@6.10.3 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index cba82afcbd2..ef5ce5fc020 100644 --- a/History.md +++ b/History.md @@ -26,6 +26,7 @@ unreleased - Remove set content headers that break response - deps: on-finished@2.4.1 - deps: statuses@2.0.1 + * deps: qs@6.10.3 * deps: send@0.18.0 - Fix emitted 416 error missing headers property - Limit the headers removed for 304 response diff --git a/package.json b/package.json index ce6604bd7dd..04e39b06e1e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.7", + "qs": "6.10.3", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", From 980d881e3b023db079de60477a2588a91f046ca5 Mon Sep 17 00:00:00 2001 From: 3imed-jaberi Date: Fri, 3 Jul 2020 05:38:59 +0200 Subject: [PATCH 334/479] deps: statuses@2.0.1 closes #4336 --- History.md | 3 +++ lib/response.js | 8 ++++---- package.json | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/History.md b/History.md index ef5ce5fc020..ef9bbb6e427 100644 --- a/History.md +++ b/History.md @@ -37,6 +37,9 @@ unreleased - deps: statuses@2.0.1 * deps: serve-static@1.15.0 - deps: send@0.18.0 + * deps: statuses@2.0.1 + - Remove code 306 + - Rename `425 Unordered Collection` to standard `425 Too Early` 4.17.3 / 2022-02-16 =================== diff --git a/lib/response.js b/lib/response.js index d9b8db1c201..fede486c06d 100644 --- a/lib/response.js +++ b/lib/response.js @@ -139,7 +139,7 @@ res.send = function send(body) { deprecate('res.send(status): Use res.sendStatus(status) instead'); this.statusCode = chunk; - chunk = statuses[chunk] + chunk = statuses.message[chunk] } switch (typeof chunk) { @@ -367,7 +367,7 @@ res.jsonp = function jsonp(obj) { */ res.sendStatus = function sendStatus(statusCode) { - var body = statuses[statusCode] || String(statusCode) + var body = statuses.message[statusCode] || String(statusCode) this.statusCode = statusCode; this.type('txt'); @@ -955,12 +955,12 @@ res.redirect = function redirect(url) { // Support text/{plain,html} by default this.format({ text: function(){ - body = statuses[status] + '. Redirecting to ' + address + body = statuses.message[status] + '. Redirecting to ' + address }, html: function(){ var u = escapeHtml(address); - body = '

      ' + statuses[status] + '. Redirecting to ' + u + '

      ' + body = '

      ' + statuses.message[status] + '. Redirecting to ' + u + '

      ' }, default: function(){ diff --git a/package.json b/package.json index 04e39b06e1e..1e1f6740977 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "send": "0.18.0", "serve-static": "1.15.0", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" From 2e2d78c4d99829250018c6e4d20f3c6377a90683 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 3 Apr 2022 01:15:37 -0400 Subject: [PATCH 335/479] deps: on-finished@2.4.1 --- History.md | 2 + package.json | 2 +- test/res.download.js | 73 ++++++++++++++++++++++++ test/res.sendFile.js | 129 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 205 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index ef9bbb6e427..84efe9b4d8a 100644 --- a/History.md +++ b/History.md @@ -26,6 +26,8 @@ unreleased - Remove set content headers that break response - deps: on-finished@2.4.1 - deps: statuses@2.0.1 + * deps: on-finished@2.4.1 + - Prevent loss of async hooks context * deps: qs@6.10.3 * deps: send@0.18.0 - Fix emitted 416 error missing headers property diff --git a/package.json b/package.json index 1e1f6740977..dfce12352ca 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", diff --git a/test/res.download.js b/test/res.download.js index 91b074e8bf6..b52e66803c6 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -1,6 +1,8 @@ 'use strict' var after = require('after'); +var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..'); var path = require('path') @@ -9,6 +11,10 @@ var utils = require('./support/utils') var FIXTURES_PATH = path.join(__dirname, 'fixtures') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('res', function(){ describe('.download(path)', function(){ it('should transfer as an attachment', function(done){ @@ -84,6 +90,65 @@ describe('res', function(){ .expect('Content-Disposition', 'attachment; filename="user.html"') .expect(200, cb); }) + + describeAsyncHooks('async local storage', function () { + it('should presist store', function (done) { + var app = express() + var cb = after(2, done) + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.download('test/fixtures/name.txt', function (err) { + if (err) return cb(err) + + var local = req.asyncLocalStorage.getStore() + + assert.strictEqual(local.foo, 'bar') + cb() + }) + }) + + request(app) + .get('/') + .expect('Content-Type', 'text/plain; charset=UTF-8') + .expect('Content-Disposition', 'attachment; filename="name.txt"') + .expect(200, 'tobi', cb) + }) + + it('should presist store on error', function (done) { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.download('test/fixtures/does-not-exist', function (err) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.send(err ? 'got ' + err.status + ' error' : 'no error') + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('got 404 error') + .end(done) + }) + }) }) describe('.download(path, options)', function () { @@ -423,3 +488,11 @@ describe('res', function(){ }) }) }) + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} diff --git a/test/res.sendFile.js b/test/res.sendFile.js index e828c17e255..ba5c33516b1 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -1,6 +1,7 @@ 'use strict' var after = require('after'); +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('../') , request = require('supertest') @@ -10,6 +11,10 @@ var path = require('path'); var fixtures = path.join(__dirname, 'fixtures'); var utils = require('./support/utils'); +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('res', function(){ describe('.sendFile(path)', function () { it('should error missing path', function (done) { @@ -261,6 +266,64 @@ describe('res', function(){ .get('/') .expect(200, 'got 404 error', done) }) + + describeAsyncHooks('async local storage', function () { + it('should presist store', function (done) { + var app = express() + var cb = after(2, done) + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { + if (err) return cb(err) + + var local = req.asyncLocalStorage.getStore() + + assert.strictEqual(local.foo, 'bar') + cb() + }) + }) + + request(app) + .get('/') + .expect('Content-Type', 'text/plain; charset=UTF-8') + .expect(200, 'tobi', cb) + }) + + it('should presist store on error', function (done) { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.send(err ? 'got ' + err.status + ' error' : 'no error') + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('got 404 error') + .end(done) + }) + }) }) describe('.sendFile(path, options)', function () { @@ -999,6 +1062,64 @@ describe('res', function(){ .get('/') .end(function(){}); }) + + describeAsyncHooks('async local storage', function () { + it('should presist store', function (done) { + var app = express() + var cb = after(2, done) + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.sendfile('test/fixtures/name.txt', function (err) { + if (err) return cb(err) + + var local = req.asyncLocalStorage.getStore() + + assert.strictEqual(local.foo, 'bar') + cb() + }) + }) + + request(app) + .get('/') + .expect('Content-Type', 'text/plain; charset=UTF-8') + .expect(200, 'tobi', cb) + }) + + it('should presist store on error', function (done) { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.sendfile('test/fixtures/does-not-exist', function (err) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.send(err ? 'got ' + err.status + ' error' : 'no error') + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('got 404 error') + .end(done) + }) + }) }) describe('.sendfile(path)', function(){ @@ -1280,3 +1401,11 @@ function createApp(path, options, fn) { return app; } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} From 04da4aaf1a484e81856fc4713340300e4d84d573 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 7 Apr 2022 19:17:10 -0400 Subject: [PATCH 336/479] build: use supertest@3.4.2 for Node.js 6.x --- .github/workflows/ci.yml | 2 +- appveyor.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b153d1b436..83a8edee87d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Node.js 6.x node-version: "6.17" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 + npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2 - name: Node.js 7.x node-version: "7.10" diff --git a/appveyor.yml b/appveyor.yml index 2a2507b411b..93ea2c81616 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -71,11 +71,11 @@ install: - ps: | # supertest for http calls # - use 2.0.0 for Node.js < 4 - # - use 3.4.2 for Node.js < 6 + # - use 3.4.2 for Node.js < 7 # - use 6.1.6 for Node.js < 8 if ([int]$env:nodejs_version.split(".")[0] -lt 4) { npm install --silent --save-dev supertest@2.0.0 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) { + } elseif ([int]$env:nodejs_version.split(".")[0] -lt 7) { npm install --silent --save-dev supertest@3.4.2 } elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) { npm install --silent --save-dev supertest@6.1.6 From 1b2e097be2f5b62b7db7dae09f399ace54836e0a Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Thu, 7 Apr 2022 21:14:16 +0530 Subject: [PATCH 337/479] tests: fix typo in description closes #4882 --- test/res.sendFile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/res.sendFile.js b/test/res.sendFile.js index ba5c33516b1..eb71adeb6a8 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -747,7 +747,7 @@ describe('res', function(){ }) describe('when cacheControl: false', function () { - it('shold not send cache-control', function (done) { + it('should not send cache-control', function (done) { var app = express() app.use(function (req, res) { From 99175c3ef63166d199bab8f402103522dec5f0ee Mon Sep 17 00:00:00 2001 From: Ghouse Mohamed Date: Sun, 27 Mar 2022 05:15:01 +0530 Subject: [PATCH 338/479] docs: fix typo in casing of HTTP closes #4872 --- Charter.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Charter.md b/Charter.md index f9647cb734d..a906e52909a 100644 --- a/Charter.md +++ b/Charter.md @@ -9,7 +9,7 @@ also easily visible to outsiders. ## Section 1: Scope -Express is a http web server framework with a simple and expressive API +Express is a HTTP web server framework with a simple and expressive API which is highly aligned with Node.js core. We aim to be the best in class for writing performant, spec compliant, and powerful web servers in Node.js. As one of the oldest and most popular web frameworks in @@ -24,7 +24,7 @@ Express is made of many modules spread between three GitHub Orgs: libraries - [pillarjs](http://github.com/pillarjs/): Components which make up Express but can also be used for other web frameworks -- [jshttp](http://github.com/jshttp/): Low level http libraries +- [jshttp](http://github.com/jshttp/): Low level HTTP libraries ### 1.2: Out-of-Scope From ecaf67c9305f3bf75a9798e8a2e10b36955df42c Mon Sep 17 00:00:00 2001 From: Eslam Salem Date: Mon, 11 Apr 2022 10:56:45 +0200 Subject: [PATCH 339/479] docs: remove Node Security Project from security policy closes #4890 --- Security.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Security.md b/Security.md index 858dfffc5bc..cdcd7a6e0aa 100644 --- a/Security.md +++ b/Security.md @@ -27,8 +27,7 @@ endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. Report security bugs in third-party modules to the person or team maintaining -the module. You can also report a vulnerability through the -[Node Security Project](https://nodesecurity.io/report). +the module. ## Disclosure Policy From b91c7ffb289af1753b9d1d84e16fbfcd34954124 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 11 Apr 2022 19:28:50 -0400 Subject: [PATCH 340/479] examples: use http-errors to create errors --- examples/params/index.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/examples/params/index.js b/examples/params/index.js index b153b93b988..b6fc483c8b7 100644 --- a/examples/params/index.js +++ b/examples/params/index.js @@ -4,6 +4,7 @@ * Module dependencies. */ +var createError = require('http-errors') var express = require('../../'); var app = module.exports = express(); @@ -17,14 +18,6 @@ var users = [ , { name: 'bandit' } ]; -// Create HTTP error - -function createError(status, message) { - var err = new Error(message); - err.status = status; - return err; -} - // Convert :to and :from to integers app.param(['to', 'from'], function(req, res, next, num, name){ From 8880ddad1c0f00612b53f5f686f55e7566b16562 Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Thu, 7 Apr 2022 21:34:47 +0530 Subject: [PATCH 341/479] examples: add missing html label associations closes #4884 --- examples/auth/views/login.ejs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/auth/views/login.ejs b/examples/auth/views/login.ejs index 8a20411a2ca..181c36caf7a 100644 --- a/examples/auth/views/login.ejs +++ b/examples/auth/views/login.ejs @@ -6,12 +6,12 @@ Try accessing /restricted, then authenticate with "tj" and "foobar".

      - - + +

      - - + +

      From 92c5ce59f51cce4b3598fd040117772fac42dce8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 11 Apr 2022 22:51:13 -0400 Subject: [PATCH 342/479] deps: cookie@0.5.0 --- History.md | 3 ++ package.json | 2 +- test/res.cookie.js | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 84efe9b4d8a..4f43ad92550 100644 --- a/History.md +++ b/History.md @@ -19,6 +19,9 @@ unreleased - deps: on-finished@2.4.1 - deps: qs@6.10.3 - deps: raw-body@2.5.1 + * deps: cookie@0.5.0 + - Add `priority` option + - Fix `expires` option to reject invalid dates * deps: depd@2.0.0 - Replace internal `eval` usage with `Function` constructor - Use instance methods on `process` to check for listeners diff --git a/package.json b/package.json index dfce12352ca..ede86798cf6 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "body-parser": "1.20.0", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.2", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", diff --git a/test/res.cookie.js b/test/res.cookie.js index e3a921301f4..93deb769887 100644 --- a/test/res.cookie.js +++ b/test/res.cookie.js @@ -67,6 +67,21 @@ describe('res', function(){ .expect(200, done) }) + describe('expires', function () { + it('should throw on invalid date', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { expires: new Date(NaN) }) + res.end() + }) + + request(app) + .get('/') + .expect(500, /option expires is invalid/, done) + }) + }) + describe('maxAge', function(){ it('should set relative expires', function(done){ var app = express(); @@ -155,6 +170,63 @@ describe('res', function(){ }) }) + describe('priority', function () { + it('should set low priority', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { priority: 'low' }) + res.end() + }) + + request(app) + .get('/') + .expect('Set-Cookie', /Priority=Low/) + .expect(200, done) + }) + + it('should set medium priority', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { priority: 'medium' }) + res.end() + }) + + request(app) + .get('/') + .expect('Set-Cookie', /Priority=Medium/) + .expect(200, done) + }) + + it('should set high priority', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { priority: 'high' }) + res.end() + }) + + request(app) + .get('/') + .expect('Set-Cookie', /Priority=High/) + .expect(200, done) + }) + + it('should throw with invalid priority', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { priority: 'foobar' }) + res.end() + }) + + request(app) + .get('/') + .expect(500, /option priority is invalid/, done) + }) + }) + describe('signed', function(){ it('should generate a signed JSON cookie', function(done){ var app = express(); From 708ac4cdf5cd0a658d62490a9f4d78d3e1ec6612 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 13 Apr 2022 23:29:25 -0400 Subject: [PATCH 343/479] Fix handling very large stacks of sync middleware closes #4891 --- History.md | 1 + lib/router/index.js | 8 ++++++++ lib/router/route.js | 9 +++++++++ test/Route.js | 22 ++++++++++++++++++++++ test/Router.js | 16 ++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/History.md b/History.md index 4f43ad92550..3f7851ba578 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ unreleased * Allow `options` without `filename` in `res.download` * Deprecate string and non-integer arguments to `res.status` * Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie` + * Fix handling very large stacks of sync middleware * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` diff --git a/lib/router/index.js b/lib/router/index.js index 791a600f86a..f4c8c0a79ef 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -142,6 +142,7 @@ proto.handle = function handle(req, res, out) { var protohost = getProtohost(req.url) || '' var removed = ''; var slashAdded = false; + var sync = 0 var paramcalled = {}; // store options for OPTIONS request @@ -203,6 +204,11 @@ proto.handle = function handle(req, res, out) { return; } + // max sync stack + if (++sync > 100) { + return setImmediate(next, err) + } + // get pathname of request var path = getPathname(req); @@ -321,6 +327,8 @@ proto.handle = function handle(req, res, out) { } else { layer.handle_request(req, res, next); } + + sync = 0 } }; diff --git a/lib/router/route.js b/lib/router/route.js index 178df0d5160..5adaa125e27 100644 --- a/lib/router/route.js +++ b/lib/router/route.js @@ -98,6 +98,8 @@ Route.prototype._options = function _options() { Route.prototype.dispatch = function dispatch(req, res, done) { var idx = 0; var stack = this.stack; + var sync = 0 + if (stack.length === 0) { return done(); } @@ -127,6 +129,11 @@ Route.prototype.dispatch = function dispatch(req, res, done) { return done(err); } + // max sync stack + if (++sync > 100) { + return setImmediate(next, err) + } + if (layer.method && layer.method !== method) { return next(err); } @@ -136,6 +143,8 @@ Route.prototype.dispatch = function dispatch(req, res, done) { } else { layer.handle_request(req, res, next); } + + sync = 0 } }; diff --git a/test/Route.js b/test/Route.js index 8e7ddbdbcc1..3bdc8d7df2f 100644 --- a/test/Route.js +++ b/test/Route.js @@ -13,6 +13,28 @@ describe('Route', function(){ route.dispatch(req, {}, done) }) + it('should not stack overflow with a large sync stack', function (done) { + this.timeout(5000) // long-running test + + var req = { method: 'GET', url: '/' } + var route = new Route('/foo') + + for (var i = 0; i < 6000; i++) { + route.all(function (req, res, next) { next() }) + } + + route.get(function (req, res, next) { + req.called = true + next() + }) + + route.dispatch(req, {}, function (err) { + if (err) return done(err) + assert.ok(req.called) + done() + }) + }) + describe('.all', function(){ it('should add handler', function(done){ var req = { method: 'GET', url: '/' }; diff --git a/test/Router.js b/test/Router.js index 907b9726361..8a0654bca3c 100644 --- a/test/Router.js +++ b/test/Router.js @@ -76,6 +76,22 @@ describe('Router', function(){ router.handle({ url: '/', method: 'GET' }, { end: done }); }); + it('should not stack overflow with a large sync stack', function (done) { + this.timeout(5000) // long-running test + + var router = new Router() + + for (var i = 0; i < 6000; i++) { + router.use(function (req, res, next) { next() }) + } + + router.use(function (req, res) { + res.end() + }) + + router.handle({ url: '/', method: 'GET' }, { end: done }) + }) + describe('.handle', function(){ it('should dispatch', function(done){ var router = new Router(); From fd8e45c344325a4a91c1b916f3617a3574018976 Mon Sep 17 00:00:00 2001 From: phoenix Date: Fri, 8 Apr 2022 10:31:27 +0200 Subject: [PATCH 344/479] tests: mark stack overflow as long running closes #4887 --- test/Router.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Router.js b/test/Router.js index 8a0654bca3c..bf5a31ffddb 100644 --- a/test/Router.js +++ b/test/Router.js @@ -62,6 +62,8 @@ describe('Router', function(){ }) it('should not stack overflow with many registered routes', function(done){ + this.timeout(5000) // long-running test + var handler = function(req, res){ res.end(new Error('wrong handler')) }; var router = new Router(); From 11a209e4b7e229bf5041e1ab76ba0ac4e0cad324 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 20 Apr 2022 22:02:37 -0400 Subject: [PATCH 345/479] build: support Node.js 17.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83a8edee87d..f4a5902ac95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,7 @@ jobs: - Node.js 14.x - Node.js 15.x - Node.js 16.x + - Node.js 17.x include: - name: Node.js 0.10 @@ -98,6 +99,9 @@ jobs: - name: Node.js 16.x node-version: "16.14" + - name: Node.js 17.x + node-version: "17.9" + steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index 93ea2c81616..f97addec1cb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,6 +18,7 @@ environment: - nodejs_version: "14.19" - nodejs_version: "15.14" - nodejs_version: "16.14" + - nodejs_version: "17.9" cache: - node_modules install: From 29ea1b2f74c5e76e79e329ef425e5fbbcd6a71c3 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Apr 2022 01:38:59 -0400 Subject: [PATCH 346/479] build: use 64-bit Node.js in AppVeyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f97addec1cb..faf31abf12f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,7 +25,7 @@ install: # Install Node.js - ps: >- try { Install-Product node $env:nodejs_version -ErrorAction Stop } - catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) } + catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64 } # Configure npm - ps: | npm config set loglevel error From 158a17031a2668269aedb31ea07b58d6b700272b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Apr 2022 02:09:08 -0400 Subject: [PATCH 347/479] build: support Node.js 18.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4a5902ac95..9d3663762c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,7 @@ jobs: - Node.js 15.x - Node.js 16.x - Node.js 17.x + - Node.js 18.x include: - name: Node.js 0.10 @@ -102,6 +103,9 @@ jobs: - name: Node.js 17.x node-version: "17.9" + - name: Node.js 18.x + node-version: "18.0" + steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index faf31abf12f..8804cfd398c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,6 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.14" - nodejs_version: "17.9" + - nodejs_version: "18.0" cache: - node_modules install: From 0b330ef57c0801313251c95a461d93f8d3afa7f7 Mon Sep 17 00:00:00 2001 From: Deniz Date: Mon, 4 Apr 2022 01:31:32 +0200 Subject: [PATCH 348/479] bench: print latency and vary connections closes #4880 --- benchmarks/Makefile | 20 ++++++++++++-------- benchmarks/run | 8 +++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/benchmarks/Makefile b/benchmarks/Makefile index baf0d6fce92..ed1ddfc4f34 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -1,13 +1,17 @@ all: - @./run 1 middleware - @./run 5 middleware - @./run 10 middleware - @./run 15 middleware - @./run 20 middleware - @./run 30 middleware - @./run 50 middleware - @./run 100 middleware + @./run 1 middleware 50 + @./run 5 middleware 50 + @./run 10 middleware 50 + @./run 15 middleware 50 + @./run 20 middleware 50 + @./run 30 middleware 50 + @./run 50 middleware 50 + @./run 100 middleware 50 + @./run 10 middleware 100 + @./run 10 middleware 250 + @./run 10 middleware 500 + @./run 10 middleware 1000 @echo .PHONY: all diff --git a/benchmarks/run b/benchmarks/run index 93b5bc52ff2..ec8f55d5643 100755 --- a/benchmarks/run +++ b/benchmarks/run @@ -4,13 +4,15 @@ echo MW=$1 node $2 & pid=$! +echo " $3 connections" + sleep 2 wrk 'http://localhost:3333/?foo[bar]=baz' \ -d 3 \ - -c 50 \ + -c $3 \ -t 8 \ - | grep 'Requests/sec' \ - | awk '{ print " " $2 }' + | grep 'Requests/sec\|Latency' \ + | awk '{ print " " $2 }' kill $pid From 547fdd41dca9ae9c49956748cc0bd1f011310fb6 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Apr 2022 14:53:28 -0400 Subject: [PATCH 349/479] 4.18.0 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 3f7851ba578..32e75b391e4 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.18.0 / 2022-04-25 +=================== * Add "root" option to `res.download` * Allow `options` without `filename` in `res.download` diff --git a/package.json b/package.json index ede86798cf6..d07cdf82b98 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.17.3", + "version": "4.18.0", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From a38fae126a9d5681d075c1a5c44fd7357eae843b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Apr 2022 13:04:11 -0400 Subject: [PATCH 350/479] build: mocha@9.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d07cdf82b98..da94b92fd4d 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "hbs": "4.2.0", "marked": "0.7.0", "method-override": "3.0.0", - "mocha": "9.2.1", + "mocha": "9.2.2", "morgan": "1.10.0", "multiparty": "4.2.3", "nyc": "15.1.0", From 2df96e349f49bbcf51126c1f3b93b3b7fe8c16d2 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Apr 2022 13:04:38 -0400 Subject: [PATCH 351/479] build: supertest@6.2.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da94b92fd4d..0499571ed4a 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", - "supertest": "6.2.2", + "supertest": "6.2.3", "vhost": "~3.0.2" }, "engines": { From e2482b7e36e39fd9875508a297c2db4a80a33635 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Apr 2022 21:59:32 -0400 Subject: [PATCH 352/479] build: ejs@3.1.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0499571ed4a..f0781ddfd48 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "connect-redis": "3.4.2", "cookie-parser": "1.4.6", "cookie-session": "2.0.0", - "ejs": "3.1.6", + "ejs": "3.1.7", "eslint": "7.32.0", "express-session": "1.17.2", "hbs": "4.2.0", From 75e0c7a2c91665f44d053d83be15f8ecd0177f41 Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Tue, 26 Apr 2022 23:23:14 +0530 Subject: [PATCH 353/479] bench: remove unused parameter closes #4898 --- benchmarks/middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js index df4df2c5ac5..fed97ba8ce4 100644 --- a/benchmarks/middleware.js +++ b/benchmarks/middleware.js @@ -13,7 +13,7 @@ while (n--) { }); } -app.use(function(req, res, next){ +app.use(function(req, res){ res.send('Hello World') }); From 631ada0c645dc84c6df8788f5a7eb2b8100acea5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 29 Apr 2022 13:34:47 -0400 Subject: [PATCH 354/479] Fix hanging on large stack of sync routes fixes #4899 --- History.md | 5 +++++ lib/router/index.js | 14 ++++++-------- test/Router.js | 18 +++++++++++++++++- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/History.md b/History.md index 32e75b391e4..b052c577f78 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Fix hanging on large stack of sync routes + 4.18.0 / 2022-04-25 =================== diff --git a/lib/router/index.js b/lib/router/index.js index f4c8c0a79ef..5174c34f455 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -279,14 +279,14 @@ proto.handle = function handle(req, res, out) { // this should be done for the layer self.process_params(layer, paramcalled, req, res, function (err) { if (err) { - return next(layerError || err); + next(layerError || err) + } else if (route) { + layer.handle_request(req, res, next) + } else { + trim_prefix(layer, layerError, layerPath, path) } - if (route) { - return layer.handle_request(req, res, next); - } - - trim_prefix(layer, layerError, layerPath, path); + sync = 0 }); } @@ -327,8 +327,6 @@ proto.handle = function handle(req, res, out) { } else { layer.handle_request(req, res, next); } - - sync = 0 } }; diff --git a/test/Router.js b/test/Router.js index bf5a31ffddb..fcfee80625c 100644 --- a/test/Router.js +++ b/test/Router.js @@ -78,7 +78,23 @@ describe('Router', function(){ router.handle({ url: '/', method: 'GET' }, { end: done }); }); - it('should not stack overflow with a large sync stack', function (done) { + it('should not stack overflow with a large sync route stack', function (done) { + this.timeout(5000) // long-running test + + var router = new Router() + + for (var i = 0; i < 6000; i++) { + router.get('/foo', function (req, res, next) { next() }) + } + + router.get('/foo', function (req, res) { + res.end() + }) + + router.handle({ url: '/foo', method: 'GET' }, { end: done }) + }) + + it('should not stack overflow with a large sync middleware stack', function (done) { this.timeout(5000) // long-running test var router = new Router() From b02a95c6937e3b7e0b85a51c7e1a7366e1699dce Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 29 Apr 2022 14:52:20 -0400 Subject: [PATCH 355/479] build: Node.js@16.15 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d3663762c6..a4b40dc982f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,7 +98,7 @@ jobs: node-version: "15.14" - name: Node.js 16.x - node-version: "16.14" + node-version: "16.15" - name: Node.js 17.x node-version: "17.9" diff --git a/appveyor.yml b/appveyor.yml index 8804cfd398c..80802e180e6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.19" - nodejs_version: "15.14" - - nodejs_version: "16.14" + - nodejs_version: "16.15" - nodejs_version: "17.9" - nodejs_version: "18.0" cache: From d854c43ea177d1faeea56189249fff8c24a764bd Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 29 Apr 2022 15:32:26 -0400 Subject: [PATCH 356/479] 4.18.1 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index b052c577f78..4c12ec97355 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.18.1 / 2022-04-29 +=================== * Fix hanging on large stack of sync routes diff --git a/package.json b/package.json index f0781ddfd48..f5872a53336 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.18.0", + "version": "4.18.1", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From a2dfc56a4982e0a33c67d6d0c22e087e95bff79e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 09:33:19 -0400 Subject: [PATCH 357/479] build: mocha@10.0.0 --- .github/workflows/ci.yml | 2 ++ appveyor.yml | 3 +++ package.json | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4b40dc982f..6df74b37f8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,9 +87,11 @@ jobs: - 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.19" diff --git a/appveyor.yml b/appveyor.yml index 80802e180e6..e9b28e7d0fa 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -47,6 +47,7 @@ install: # - use 6.x for Node.js < 8 # - use 7.x for Node.js < 10 # - use 8.x for Node.js < 12 + # - use 9.x for Node.js < 14 if ([int]$env:nodejs_version.split(".")[0] -lt 4) { npm install --silent --save-dev mocha@3.5.3 } elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) { @@ -57,6 +58,8 @@ install: npm install --silent --save-dev mocha@7.2.0 } elseif ([int]$env:nodejs_version.split(".")[0] -lt 12) { npm install --silent --save-dev mocha@8.4.0 + } elseif ([int]$env:nodejs_version.split(".")[0] -lt 14) { + npm install --silent --save-dev mocha@9.2.2 } - ps: | # nyc for test coverage diff --git a/package.json b/package.json index f5872a53336..6880db25241 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "hbs": "4.2.0", "marked": "0.7.0", "method-override": "3.0.0", - "mocha": "9.2.2", + "mocha": "10.0.0", "morgan": "1.10.0", "multiparty": "4.2.3", "nyc": "15.1.0", From 745a63f8256828a061e1b2f0a5f8e52eb9538da1 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 09:34:47 -0400 Subject: [PATCH 358/479] build: ejs@3.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6880db25241..3430f770972 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "connect-redis": "3.4.2", "cookie-parser": "1.4.6", "cookie-session": "2.0.0", - "ejs": "3.1.7", + "ejs": "3.1.8", "eslint": "7.32.0", "express-session": "1.17.2", "hbs": "4.2.0", From ab2c70b954ac2ceb3aaf466b0f59089999952dd0 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 09:35:20 -0400 Subject: [PATCH 359/479] build: Node.js@18.1 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6df74b37f8f..b7ad4578297 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.0" + node-version: "18.1" steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index e9b28e7d0fa..b78a0b1550d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.15" - nodejs_version: "17.9" - - nodejs_version: "18.0" + - nodejs_version: "18.1" cache: - node_modules install: From 7ec5dd2b3c5e7379f68086dae72859f5573c8b9b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 09:37:20 -0400 Subject: [PATCH 360/479] Fix regression routing a large stack in a single route fixes #4913 --- History.md | 5 +++++ lib/router/route.js | 16 ++++++++-------- test/Route.js | 11 ++++++++++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/History.md b/History.md index 4c12ec97355..cbf4b5249fa 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Fix regression routing a large stack in a single route + 4.18.1 / 2022-04-29 =================== diff --git a/lib/router/route.js b/lib/router/route.js index 5adaa125e27..cc643ac8bdb 100644 --- a/lib/router/route.js +++ b/lib/router/route.js @@ -124,21 +124,21 @@ Route.prototype.dispatch = function dispatch(req, res, done) { return done(err) } - var layer = stack[idx++]; - if (!layer) { - return done(err); - } - // max sync stack if (++sync > 100) { return setImmediate(next, err) } - if (layer.method && layer.method !== method) { - return next(err); + var layer = stack[idx++] + + // end of layers + if (!layer) { + return done(err) } - if (err) { + if (layer.method && layer.method !== method) { + next(err) + } else if (err) { layer.handle_error(err, req, res, next); } else { layer.handle_request(req, res, next); diff --git a/test/Route.js b/test/Route.js index 3bdc8d7df2f..64dbad60ce9 100644 --- a/test/Route.js +++ b/test/Route.js @@ -19,8 +19,16 @@ describe('Route', function(){ var req = { method: 'GET', url: '/' } var route = new Route('/foo') + route.get(function (req, res, next) { + req.counter = 0 + next() + }) + for (var i = 0; i < 6000; i++) { - route.all(function (req, res, next) { next() }) + route.all(function (req, res, next) { + req.counter++ + next() + }) } route.get(function (req, res, next) { @@ -31,6 +39,7 @@ describe('Route', function(){ route.dispatch(req, {}, function (err) { if (err) return done(err) assert.ok(req.called) + assert.strictEqual(req.counter, 6000) done() }) }) From 97f0a518d8d697e310abf293a71383cf9d04d749 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 11:54:35 -0400 Subject: [PATCH 361/479] tests: verify all handlers called in stack tests --- test/Router.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/Router.js b/test/Router.js index fcfee80625c..0d0502ab40d 100644 --- a/test/Router.js +++ b/test/Router.js @@ -83,11 +83,20 @@ describe('Router', function(){ var router = new Router() + router.get('/foo', function (req, res, next) { + req.counter = 0 + next() + }) + for (var i = 0; i < 6000; i++) { - router.get('/foo', function (req, res, next) { next() }) + router.get('/foo', function (req, res, next) { + req.counter++ + next() + }) } router.get('/foo', function (req, res) { + assert.strictEqual(req.counter, 6000) res.end() }) @@ -99,11 +108,20 @@ describe('Router', function(){ var router = new Router() + router.use(function (req, res, next) { + req.counter = 0 + next() + }) + for (var i = 0; i < 6000; i++) { - router.use(function (req, res, next) { next() }) + router.use(function (req, res, next) { + req.counter++ + next() + }) } router.use(function (req, res) { + assert.strictEqual(req.counter, 6000) res.end() }) From 2c47827053233e707536019a15499ccf5496dc9d Mon Sep 17 00:00:00 2001 From: Alexandru Dragomir Date: Fri, 20 May 2022 18:49:44 +0300 Subject: [PATCH 362/479] examples: remove unused function arguments in params closes #4914 --- examples/params/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/params/index.js b/examples/params/index.js index b6fc483c8b7..f3cd8457eb5 100644 --- a/examples/params/index.js +++ b/examples/params/index.js @@ -51,7 +51,7 @@ app.get('/', function(req, res){ * GET :user. */ -app.get('/user/:user', function(req, res, next){ +app.get('/user/:user', function (req, res) { res.send('user ' + req.user.name); }); @@ -59,7 +59,7 @@ app.get('/user/:user', function(req, res, next){ * GET users :from - :to. */ -app.get('/users/:from-:to', function(req, res, next){ +app.get('/users/:from-:to', function (req, res) { var from = req.params.from; var to = req.params.to; var names = users.map(function(user){ return user.name; }); From 8d98e86d7fe4e4dd50e42e73301b0bb7b7132758 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 18 Aug 2022 23:00:36 -0400 Subject: [PATCH 363/479] build: Node.js@16.17 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7ad4578297..5be813b2fed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: node-version: "15.14" - name: Node.js 16.x - node-version: "16.15" + node-version: "16.17" - name: Node.js 17.x node-version: "17.9" diff --git a/appveyor.yml b/appveyor.yml index b78a0b1550d..ef183ed7d75 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.19" - nodejs_version: "15.14" - - nodejs_version: "16.15" + - nodejs_version: "16.17" - nodejs_version: "17.9" - nodejs_version: "18.1" cache: From 97131bcda8bd3cdbe53ef14fbd08dcc23a53e758 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 18 Aug 2022 23:01:25 -0400 Subject: [PATCH 364/479] build: Node.js@18.7 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5be813b2fed..1ad8d117668 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.1" + node-version: "18.7" steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index ef183ed7d75..071f0de0924 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.17" - nodejs_version: "17.9" - - nodejs_version: "18.1" + - nodejs_version: "18.7" cache: - node_modules install: From ecd7572f1e920b7a512452b8d9806ae617a99c54 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 18 Aug 2022 23:41:10 -0400 Subject: [PATCH 365/479] build: Node.js@14.20 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ad8d117668..5d2cef5a4d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: npm-i: mocha@9.2.2 - name: Node.js 14.x - node-version: "14.19" + node-version: "14.20" - name: Node.js 15.x node-version: "15.14" diff --git a/appveyor.yml b/appveyor.yml index 071f0de0924..7bf0141b380 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,7 +15,7 @@ environment: - nodejs_version: "11.15" - nodejs_version: "12.22" - nodejs_version: "13.14" - - nodejs_version: "14.19" + - nodejs_version: "14.20" - nodejs_version: "15.14" - nodejs_version: "16.17" - nodejs_version: "17.9" From 644f6464b9f61cbafa8f880636b1aa5237d95bad Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 18 Aug 2022 23:42:39 -0400 Subject: [PATCH 366/479] build: supertest@6.2.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3430f770972..cbfa910ff09 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", - "supertest": "6.2.3", + "supertest": "6.2.4", "vhost": "~3.0.2" }, "engines": { From 33e8dc303af9277f8a7e4f46abfdcb5e72f6797b Mon Sep 17 00:00:00 2001 From: REALSTEVEIG <101066723+REALSTEVEIG@users.noreply.github.com> Date: Sat, 11 Jun 2022 21:26:10 +0100 Subject: [PATCH 367/479] docs: use Node.js name style closes #4926 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 720bf389224..9b8bc34a0ce 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ [![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/) - Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). + Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org). [![NPM Version][npm-version-image]][npm-url] [![NPM Install Size][npm-install-size-image]][npm-install-size-url] From 340be0f79afb9b3176afb76235aa7f92acbd5050 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 5 Oct 2022 22:40:51 -0400 Subject: [PATCH 368/479] build: eslint@8.24.0 --- .github/workflows/ci.yml | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d2cef5a4d1..6125da491ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,8 +133,8 @@ jobs: shell: bash run: | # eslint for linting - # - remove on Node.js < 10 - if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then + # - 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 | \ diff --git a/package.json b/package.json index cbfa910ff09..defab0eec0d 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "cookie-parser": "1.4.6", "cookie-session": "2.0.0", "ejs": "3.1.8", - "eslint": "7.32.0", + "eslint": "8.24.0", "express-session": "1.17.2", "hbs": "4.2.0", "marked": "0.7.0", From 689d175b8b39d8860b81d723233fb83d15201827 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Oct 2022 10:26:18 -0400 Subject: [PATCH 369/479] deps: body-parser@1.20.1 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index cbf4b5249fa..389c837b089 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,9 @@ unreleased ========== * Fix regression routing a large stack in a single route + * deps: body-parser@1.20.1 + - deps: qs@6.11.0 + - perf: remove unnecessary object clone 4.18.1 / 2022-04-29 =================== diff --git a/package.json b/package.json index defab0eec0d..2c2d40a73f7 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", From 24b3dc551670ac4fb0cd5a2bd5ef643c9525e60f Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Oct 2022 10:27:01 -0400 Subject: [PATCH 370/479] deps: qs@6.11.0 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 389c837b089..a05baee6179 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ unreleased * deps: body-parser@1.20.1 - deps: qs@6.11.0 - perf: remove unnecessary object clone + * deps: qs@6.11.0 4.18.1 / 2022-04-29 =================== diff --git a/package.json b/package.json index 2c2d40a73f7..a7815d9fbee 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", From f56ce73186e885a938bfdb3d3d1005a58e6ae12b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Oct 2022 10:28:13 -0400 Subject: [PATCH 371/479] build: supertest@6.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a7815d9fbee..6a11013c72c 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", - "supertest": "6.2.4", + "supertest": "6.3.0", "vhost": "~3.0.2" }, "engines": { From bb7907b932afe3a19236a642f6054b6c8f7349a0 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 7 Oct 2022 17:48:59 -0400 Subject: [PATCH 372/479] build: Node.js@18.10 closes #5014 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- test/res.sendFile.js | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6125da491ad..cd93ab223d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.7" + node-version: "18.10" steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index 7bf0141b380..1fca21801e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.17" - nodejs_version: "17.9" - - nodejs_version: "18.7" + - nodejs_version: "18.10" cache: - node_modules install: diff --git a/test/res.sendFile.js b/test/res.sendFile.js index eb71adeb6a8..4db0a3b6a4e 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -1050,12 +1050,13 @@ describe('res', function(){ app.use(function(req, res){ res.sendfile('test/fixtures/user.html', function(err){ - assert(!res.headersSent); - assert.strictEqual(req.socket.listeners('error').length, 1) // node's original handler + assert.ok(err) + assert.ok(!res.headersSent) + assert.strictEqual(err.message, 'broken!') done(); }); - req.socket.emit('error', new Error('broken!')); + req.socket.destroy(new Error('broken!')) }); request(app) From 61f40491222dbede653b9938e6a4676f187aab44 Mon Sep 17 00:00:00 2001 From: Abhinav Das Date: Sat, 8 Oct 2022 00:32:42 +0530 Subject: [PATCH 373/479] docs: replace Freenode with Libera Chat closes #5013 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 9b8bc34a0ce..0936816bedb 100644 --- a/Readme.md +++ b/Readme.md @@ -51,7 +51,7 @@ for more information. ## Docs & Community * [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)] - * [#express](https://webchat.freenode.net/?channels=express) on freenode IRC + * [#express](https://web.libera.chat/#express) on [Libera Chat](https://libera.chat) IRC * [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules * Visit the [Wiki](https://github.com/expressjs/express/wiki) * [Google Group](https://groups.google.com/group/express-js) for discussion From 8368dc178af16b91b576c4c1d135f701a0007e5d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 8 Oct 2022 16:11:42 -0400 Subject: [PATCH 374/479] 4.18.2 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index a05baee6179..e49870fed0b 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.18.2 / 2022-10-08 +=================== * Fix regression routing a large stack in a single route * deps: body-parser@1.20.1 diff --git a/package.json b/package.json index 6a11013c72c..0996637deaa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.18.1", + "version": "4.18.2", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 06b2b1416d07698b8a6eed467f90d0b3ceb380c8 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 31 Oct 2022 16:39:42 -0400 Subject: [PATCH 375/479] docs: update git clone to https protocol closes #5032 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 0936816bedb..d0f3cf56e6d 100644 --- a/Readme.md +++ b/Readme.md @@ -104,7 +104,7 @@ $ npm start To view the examples, clone the Express repo and install the dependencies: ```console -$ git clone git://github.com/expressjs/express.git --depth 1 +$ git clone https://github.com/expressjs/express.git --depth 1 $ cd express $ npm install ``` From 29e117e676901a804031896f95f0eba317b05099 Mon Sep 17 00:00:00 2001 From: Arnaud Benhamdine Date: Tue, 1 Nov 2022 22:34:11 +0100 Subject: [PATCH 376/479] build: Node.js@16.18 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd93ab223d2..33da666df3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: node-version: "15.14" - name: Node.js 16.x - node-version: "16.17" + node-version: "16.18" - name: Node.js 17.x node-version: "17.9" diff --git a/appveyor.yml b/appveyor.yml index 1fca21801e8..5c55ace4a05 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.20" - nodejs_version: "15.14" - - nodejs_version: "16.17" + - nodejs_version: "16.18" - nodejs_version: "17.9" - nodejs_version: "18.10" cache: From 723b67766fb864424a59ebe46b6516bb484f6a23 Mon Sep 17 00:00:00 2001 From: Arnaud Benhamdine Date: Tue, 1 Nov 2022 22:34:11 +0100 Subject: [PATCH 377/479] build: Node.js@18.12 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33da666df3f..868525a4b1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.10" + node-version: "18.12" steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index 5c55ace4a05..02a5c1169d5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.18" - nodejs_version: "17.9" - - nodejs_version: "18.10" + - nodejs_version: "18.12" cache: - node_modules install: From 442fd467992992558806da8da07e945838712587 Mon Sep 17 00:00:00 2001 From: Abdul Rauf Date: Wed, 19 Oct 2022 18:05:06 +0500 Subject: [PATCH 378/479] build: actions/checkout@v3 closes #5027 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 868525a4b1b..b6a2879603d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,7 @@ jobs: node-version: "18.12" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Node.js ${{ matrix.node-version }} shell: bash -eo pipefail -l {0} From c6ee8d6e7f11c3ac6bdda8e1bd4c1e38445f2d22 Mon Sep 17 00:00:00 2001 From: Rakesh Bisht Date: Fri, 10 Feb 2023 11:50:16 +0530 Subject: [PATCH 379/479] lint: remove unused function arguments in tests closes #5124 --- test/Router.js | 2 +- test/app.param.js | 6 +++--- test/app.router.js | 22 +++++++++++----------- test/res.format.js | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/Router.js b/test/Router.js index 0d0502ab40d..620c60278a1 100644 --- a/test/Router.js +++ b/test/Router.js @@ -201,7 +201,7 @@ describe('Router', function(){ it('should handle throwing inside routes with params', function(done) { var router = new Router(); - router.get('/foo/:id', function(req, res, next){ + router.get('/foo/:id', function () { throw new Error('foo'); }); diff --git a/test/app.param.js b/test/app.param.js index 8893851f9d5..b4ccc8a2d12 100644 --- a/test/app.param.js +++ b/test/app.param.js @@ -166,7 +166,7 @@ describe('app', function(){ app.get('/:user', function(req, res, next) { next('route'); }); - app.get('/:user', function(req, res, next) { + app.get('/:user', function (req, res) { res.send(req.params.user); }); @@ -187,11 +187,11 @@ describe('app', function(){ next(new Error('invalid invocation')) }); - app.post('/:user', function(req, res, next) { + app.post('/:user', function (req, res) { res.send(req.params.user); }); - app.get('/:thing', function(req, res, next) { + app.get('/:thing', function (req, res) { res.send(req.thing); }); diff --git a/test/app.router.js b/test/app.router.js index 3069a22c772..4fde03105c6 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -90,7 +90,7 @@ describe('app.router', function(){ it('should decode correct params', function(done){ var app = express(); - app.get('/:name', function(req, res, next){ + app.get('/:name', function (req, res) { res.send(req.params.name); }); @@ -102,7 +102,7 @@ describe('app.router', function(){ it('should not accept params in malformed paths', function(done) { var app = express(); - app.get('/:name', function(req, res, next){ + app.get('/:name', function (req, res) { res.send(req.params.name); }); @@ -114,7 +114,7 @@ describe('app.router', function(){ it('should not decode spaces', function(done) { var app = express(); - app.get('/:name', function(req, res, next){ + app.get('/:name', function (req, res) { res.send(req.params.name); }); @@ -126,7 +126,7 @@ describe('app.router', function(){ it('should work with unicode', function(done) { var app = express(); - app.get('/:name', function(req, res, next){ + app.get('/:name', function (req, res) { res.send(req.params.name); }); @@ -910,7 +910,7 @@ describe('app.router', function(){ next(); }); - app.get('/bar', function(req, res){ + app.get('/bar', function () { assert(0); }); @@ -919,7 +919,7 @@ describe('app.router', function(){ next(); }); - app.get('/foo', function(req, res, next){ + app.get('/foo', function (req, res) { calls.push('/foo 2'); res.json(calls) }); @@ -939,7 +939,7 @@ describe('app.router', function(){ next('route') } - app.get('/foo', fn, function(req, res, next){ + app.get('/foo', fn, function (req, res) { res.end('failure') }); @@ -964,11 +964,11 @@ describe('app.router', function(){ next('router') } - router.get('/foo', fn, function (req, res, next) { + router.get('/foo', fn, function (req, res) { res.end('failure') }) - router.get('/foo', function (req, res, next) { + router.get('/foo', function (req, res) { res.end('failure') }) @@ -995,7 +995,7 @@ describe('app.router', function(){ next(); }); - app.get('/bar', function(req, res){ + app.get('/bar', function () { assert(0); }); @@ -1004,7 +1004,7 @@ describe('app.router', function(){ next(new Error('fail')); }); - app.get('/foo', function(req, res, next){ + app.get('/foo', function () { assert(0); }); diff --git a/test/res.format.js b/test/res.format.js index 45243d17a1b..cba6fe136b1 100644 --- a/test/res.format.js +++ b/test/res.format.js @@ -61,7 +61,7 @@ app3.use(function(req, res, next){ var app4 = express(); -app4.get('/', function(req, res, next){ +app4.get('/', function (req, res) { res.format({ text: function(){ res.send('hey') }, html: function(){ res.send('

      hey

      ') }, @@ -155,7 +155,7 @@ describe('res', function(){ var app = express(); var router = express.Router(); - router.get('/', function(req, res, next){ + router.get('/', function (req, res) { res.format({ text: function(){ res.send('hey') }, html: function(){ res.send('

      hey

      ') }, From a1efd9d6cf968a9e863f3fdd3fef63d06ff039c4 Mon Sep 17 00:00:00 2001 From: Rakesh Bisht Date: Wed, 8 Feb 2023 16:13:20 +0530 Subject: [PATCH 380/479] lint: remove unused parameter from internal function coses #5119 --- lib/utils.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 799a6a2b4ee..ab22f61e966 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -117,17 +117,15 @@ exports.contentDisposition = deprecate.function(contentDisposition, /** * Parse accept params `str` returning an * object with `.value`, `.quality` and `.params`. - * also includes `.originalIndex` for stable sorting * * @param {String} str - * @param {Number} index * @return {Object} * @api private */ -function acceptParams(str, index) { +function acceptParams (str) { var parts = str.split(/ *; */); - var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index }; + var ret = { value: parts[0], quality: 1, params: {} } for (var i = 1; i < parts.length; ++i) { var pms = parts[i].split(/ *= */); From 6b4c4f5426fb5de23fb7174fd6e3bf53048e06ca Mon Sep 17 00:00:00 2001 From: Rakesh Bisht Date: Wed, 8 Feb 2023 15:56:13 +0530 Subject: [PATCH 381/479] docs: fix typos in JSDoc comments closes #5117 --- examples/markdown/index.js | 2 +- lib/router/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/markdown/index.js b/examples/markdown/index.js index 74ac05e77f3..23d645e66b2 100644 --- a/examples/markdown/index.js +++ b/examples/markdown/index.js @@ -26,7 +26,7 @@ app.engine('md', function(path, options, fn){ app.set('views', path.join(__dirname, 'views')); -// make it the default so we dont need .md +// make it the default, so we don't need .md app.set('view engine', 'md'); app.get('/', function(req, res){ diff --git a/lib/router/index.js b/lib/router/index.js index 5174c34f455..abb3a6f589e 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -36,7 +36,7 @@ var toString = Object.prototype.toString; * Initialize a new `Router` with the given `options`. * * @param {Object} [options] - * @return {Router} which is an callable function + * @return {Router} which is a callable function * @public */ From 3c1d605da76a6c25dbe423a42d58871803c3e328 Mon Sep 17 00:00:00 2001 From: Rakesh Bisht Date: Fri, 3 Feb 2023 20:34:39 +0530 Subject: [PATCH 382/479] lint: remove unused parameters in examples closes #5113 --- examples/error/index.js | 2 +- examples/view-locals/index.js | 4 ++-- examples/web-service/index.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/error/index.js b/examples/error/index.js index d922de06cc4..d733a81172d 100644 --- a/examples/error/index.js +++ b/examples/error/index.js @@ -26,7 +26,7 @@ function error(err, req, res, next) { res.send('Internal Server Error'); } -app.get('/', function(req, res){ +app.get('/', function () { // Caught and passed down to the errorHandler middleware throw new Error('something broke!'); }); diff --git a/examples/view-locals/index.js b/examples/view-locals/index.js index bf52d2a85ad..a2af24f3553 100644 --- a/examples/view-locals/index.js +++ b/examples/view-locals/index.js @@ -61,7 +61,7 @@ function users(req, res, next) { }) } -app.get('/middleware', count, users, function(req, res, next){ +app.get('/middleware', count, users, function (req, res) { res.render('index', { title: 'Users', count: req.count, @@ -99,7 +99,7 @@ function users2(req, res, next) { }) } -app.get('/middleware-locals', count2, users2, function(req, res, next){ +app.get('/middleware-locals', count2, users2, function (req, res) { // you can see now how we have much less // to pass to res.render(). If we have // several routes related to users this diff --git a/examples/web-service/index.js b/examples/web-service/index.js index a2cd2cb7f90..d1a90362153 100644 --- a/examples/web-service/index.js +++ b/examples/web-service/index.js @@ -72,12 +72,12 @@ var userRepos = { // and simply expose the data // example: http://localhost:3000/api/users/?api-key=foo -app.get('/api/users', function(req, res, next){ +app.get('/api/users', function (req, res) { res.send(users); }); // example: http://localhost:3000/api/repos/?api-key=foo -app.get('/api/repos', function(req, res, next){ +app.get('/api/repos', function (req, res) { res.send(repos); }); From f05b5d0e9c43625e5677b427c33b2f950eb5bea8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 22 Feb 2023 11:59:40 -0500 Subject: [PATCH 383/479] build: Node.js@16.19 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6a2879603d..332ce4394ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: node-version: "15.14" - name: Node.js 16.x - node-version: "16.18" + node-version: "16.19" - name: Node.js 17.x node-version: "17.9" diff --git a/appveyor.yml b/appveyor.yml index 02a5c1169d5..4c8722ed9f0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.20" - nodejs_version: "15.14" - - nodejs_version: "16.18" + - nodejs_version: "16.19" - nodejs_version: "17.9" - nodejs_version: "18.12" cache: From 546969d1989d00fda460ccb23fabb943650dac51 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 22 Feb 2023 12:07:48 -0500 Subject: [PATCH 384/479] build: Node.js@18.14 --- .github/workflows/ci.yml | 8 ++++++-- appveyor.yml | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 332ce4394ca..64538c290c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.12" + node-version: "18.14" steps: - uses: actions/checkout@v3 @@ -120,7 +120,11 @@ jobs: - name: Configure npm run: | npm config set loglevel error - npm config set shrinkwrap false + if [[ "$(npm config get package-lock)" == "true" ]]; then + npm config set package-lock false + else + npm config set shrinkwrap false + fi - name: Install npm module(s) ${{ matrix.npm-i }} run: npm install --save-dev ${{ matrix.npm-i }} diff --git a/appveyor.yml b/appveyor.yml index 4c8722ed9f0..47941052bfb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.19" - nodejs_version: "17.9" - - nodejs_version: "18.12" + - nodejs_version: "18.14" cache: - node_modules install: @@ -30,7 +30,11 @@ install: # Configure npm - ps: | npm config set loglevel error - npm config set shrinkwrap false + if ((npm config get package-lock) -eq "true") { + npm config set package-lock false + } else { + npm config set shrinkwrap false + } # Remove all non-test dependencies - ps: | # Remove example dependencies From b9f7a97fe164d2a64279105abe375c69eaab9ebb Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 22 Feb 2023 18:10:59 -0500 Subject: [PATCH 385/479] build: use $GITHUB_OUTPUT for environment list --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64538c290c3..a4bf538948f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,7 +155,7 @@ jobs: echo "node@$(node -v)" echo "npm@$(npm -v)" npm -s ls ||: - (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print "::set-output name=" $2 "::" $3 }' + (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT" - name: Run tests shell: bash From 506fbd63befe810783dba49d11159c7ad46c239a Mon Sep 17 00:00:00 2001 From: Rakesh Bisht Date: Wed, 22 Feb 2023 18:30:50 +0530 Subject: [PATCH 386/479] docs: add missing JSDoc param for parseExtendedQueryString closes #5130 --- lib/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils.js b/lib/utils.js index ab22f61e966..56e12b9b541 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -280,6 +280,7 @@ function createETagGenerator (options) { /** * Parse an extended query string with qs. * + * @param {String} str * @return {Object} * @private */ From 1e42a98db6708e5a3609d0f7c09bcc176b481ea7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 23 Feb 2023 00:24:20 -0500 Subject: [PATCH 387/479] deps: body-parser@1.20.2 --- History.md | 8 ++++++++ package.json | 2 +- test/express.json.js | 8 ++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/History.md b/History.md index e49870fed0b..7b0cc4abf78 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,11 @@ +unreleased +========== + + * deps: body-parser@1.20.2 + - Fix strict json error message on Node.js 19+ + - deps: content-type@~1.0.5 + - deps: raw-body@2.5.2 + 4.18.2 / 2022-10-08 =================== diff --git a/package.json b/package.json index 0996637deaa..adf479dfce6 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", diff --git a/test/express.json.js b/test/express.json.js index a8cfebc41e2..f6f536b15e5 100644 --- a/test/express.json.js +++ b/test/express.json.js @@ -262,7 +262,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('true') - .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done) }) }) @@ -290,7 +290,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('true') - .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done) }) it('should not parse primitives with leading whitespaces', function (done) { @@ -298,7 +298,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send(' true') - .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done) }) it('should allow leading whitespaces in JSON', function (done) { @@ -316,7 +316,7 @@ describe('express.json()', function () { .set('X-Error-Property', 'stack') .send('true') .expect(400) - .expect(shouldContainInBody(parseError('#rue').replace('#', 't'))) + .expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't'))) .end(done) }) }) From 60b7c672c19ba6b96cc7e5383eee00f8bf99a45a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 23 Feb 2023 00:27:06 -0500 Subject: [PATCH 388/479] build: mocha@10.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index adf479dfce6..c3641a02c82 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "hbs": "4.2.0", "marked": "0.7.0", "method-override": "3.0.0", - "mocha": "10.0.0", + "mocha": "10.2.0", "morgan": "1.10.0", "multiparty": "4.2.3", "nyc": "15.1.0", From 8a76f39d9844f36797cd794fb74b47c635798ae5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 23 Feb 2023 00:28:41 -0500 Subject: [PATCH 389/479] build: eslint@8.34.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c3641a02c82..e4ed037cfa7 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "cookie-parser": "1.4.6", "cookie-session": "2.0.0", "ejs": "3.1.8", - "eslint": "8.24.0", + "eslint": "8.34.0", "express-session": "1.17.2", "hbs": "4.2.0", "marked": "0.7.0", From 5ad95419bac1cb5bcc1f09fd1872f2b2af4aed1a Mon Sep 17 00:00:00 2001 From: Rakesh Bisht Date: Thu, 23 Feb 2023 11:36:40 +0530 Subject: [PATCH 390/479] docs: fix typos in history closes #5131 --- History.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/History.md b/History.md index 7b0cc4abf78..17b03705df4 100644 --- a/History.md +++ b/History.md @@ -2119,7 +2119,7 @@ unreleased * deps: connect@2.21.0 - deprecate `connect(middleware)` -- use `app.use(middleware)` instead - deprecate `connect.createServer()` -- use `connect()` instead - - fix `res.setHeader()` patch to work with with get -> append -> set pattern + - fix `res.setHeader()` patch to work with get -> append -> set pattern - deps: compression@~1.0.8 - deps: errorhandler@~1.1.1 - deps: express-session@~1.5.0 @@ -3330,8 +3330,8 @@ Shaw] * Added node v0.1.97 compatibility * Added support for deleting cookies via Request#cookie('key', null) * Updated haml submodule - * Fixed not-found page, now using using charset utf-8 - * Fixed show-exceptions page, now using using charset utf-8 + * Fixed not-found page, now using charset utf-8 + * Fixed show-exceptions page, now using charset utf-8 * Fixed view support due to fs.readFile Buffers * Changed; mime.type() no longer accepts ".type" due to node extname() changes @@ -3366,7 +3366,7 @@ Shaw] ================== * Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s - encoding is set to 'utf8' or 'utf-8'. + encoding is set to 'utf8' or 'utf-8'). * Added "encoding" option to Request#render(). Closes #299 * Added "dump exceptions" setting, which is enabled by default. * Added simple ejs template engine support @@ -3405,7 +3405,7 @@ Shaw] * Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js * Added callback function support to Request#halt() as 3rd/4th arg * Added preprocessing of route param wildcards using param(). Closes #251 - * Added view partial support (with collections etc) + * Added view partial support (with collections etc.) * Fixed bug preventing falsey params (such as ?page=0). Closes #286 * Fixed setting of multiple cookies. Closes #199 * Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml) From 9bc1742937253825d2dc1e9a48c8e8424f0a315b Mon Sep 17 00:00:00 2001 From: Arnaud Benhamdine Date: Thu, 23 Feb 2023 14:18:57 +0100 Subject: [PATCH 391/479] build: support Node.js 19.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4bf538948f..c6fb81de93f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,7 @@ jobs: - Node.js 16.x - Node.js 17.x - Node.js 18.x + - Node.js 19.x include: - name: Node.js 0.10 @@ -108,6 +109,9 @@ jobs: - name: Node.js 18.x node-version: "18.14" + - name: Node.js 19.x + node-version: "19.7" + steps: - uses: actions/checkout@v3 diff --git a/appveyor.yml b/appveyor.yml index 47941052bfb..ac671ef47bc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,6 +20,7 @@ environment: - nodejs_version: "16.19" - nodejs_version: "17.9" - nodejs_version: "18.14" + - nodejs_version: "19.7" cache: - node_modules install: From 74beeac0718c928b4ba249aba3652c52fbe32ca8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 23 Feb 2023 17:23:22 -0500 Subject: [PATCH 392/479] Fix routing requests without method --- History.md | 1 + lib/router/route.js | 9 +++++++-- test/Router.js | 27 +++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 17b03705df4..9e85df3ceb2 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Fix routing requests without method * deps: body-parser@1.20.2 - Fix strict json error message on Node.js 19+ - deps: content-type@~1.0.5 diff --git a/lib/router/route.js b/lib/router/route.js index cc643ac8bdb..a65756d6de6 100644 --- a/lib/router/route.js +++ b/lib/router/route.js @@ -60,7 +60,10 @@ Route.prototype._handles_method = function _handles_method(method) { return true; } - var name = method.toLowerCase(); + // normalize name + var name = typeof method === 'string' + ? method.toLowerCase() + : method if (name === 'head' && !this.methods['head']) { name = 'get'; @@ -103,8 +106,10 @@ Route.prototype.dispatch = function dispatch(req, res, done) { if (stack.length === 0) { return done(); } + var method = typeof req.method === 'string' + ? req.method.toLowerCase() + : req.method - var method = req.method.toLowerCase(); if (method === 'head' && !this.methods['head']) { method = 'get'; } diff --git a/test/Router.js b/test/Router.js index 620c60278a1..cf5b5c1f0d3 100644 --- a/test/Router.js +++ b/test/Router.js @@ -61,6 +61,33 @@ describe('Router', function(){ router.handle({ method: 'GET' }, {}, done) }) + it('handle missing method', function (done) { + var all = false + var router = new Router() + var route = router.route('/foo') + var use = false + + route.post(function (req, res, next) { next(new Error('should not run')) }) + route.all(function (req, res, next) { + all = true + next() + }) + route.get(function (req, res, next) { next(new Error('should not run')) }) + + router.get('/foo', function (req, res, next) { next(new Error('should not run')) }) + router.use(function (req, res, next) { + use = true + next() + }) + + router.handle({ url: '/foo' }, {}, function (err) { + if (err) return done(err) + assert.ok(all) + assert.ok(use) + done() + }) + }) + it('should not stack overflow with many registered routes', function(done){ this.timeout(5000) // long-running test From 0debedf4f31bb20203da0534719b9b10d6ac9a29 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 26 Feb 2023 13:34:32 -0500 Subject: [PATCH 393/479] build: fix code coverage aggregate upload --- .github/workflows/ci.yml | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6fb81de93f..b30c56c2f12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,25 +163,48 @@ jobs: - name: Run tests shell: bash - run: npm run test-ci + run: | + 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 - uses: coverallsapp/github-action@master + run: | + mv ./coverage "./${{ matrix.name }}" + mkdir ./coverage + mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}" + + - name: Upload code coverage + uses: actions/upload-artifact@v3 with: - github-token: ${{ secrets.GITHUB_TOKEN }} - flag-name: run-${{ matrix.test_number }} - parallel: true + name: coverage + path: ./coverage + retention-days: 1 coverage: needs: test runs-on: ubuntu-latest steps: - - name: Upload code coverage + - uses: actions/checkout@v3 + + - name: Install lcov + shell: bash + run: sudo apt-get -y install lcov + + - name: Collect coverage reports + uses: actions/download-artifact@v3 + with: + name: coverage + path: ./coverage + + - name: Merge coverage reports + shell: bash + run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./coverage/lcov.info + + - name: Upload coverage report uses: coverallsapp/github-action@master with: - github-token: ${{ secrets.github_token }} - parallel-finished: true + github-token: ${{ secrets.GITHUB_TOKEN }} From 8c24fa8f7b6d443869c655166c93869d8b299627 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 13 Mar 2023 22:43:19 -0400 Subject: [PATCH 394/479] tests: wait for server close in app.listen() --- test/app.listen.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/app.listen.js b/test/app.listen.js index 08eeaaa63c2..0eec582e69a 100644 --- a/test/app.listen.js +++ b/test/app.listen.js @@ -7,8 +7,7 @@ describe('app.listen()', function(){ var app = express(); var server = app.listen(9999, function(){ - server.close(); - done(); + server.close(done) }); }) }) From f4e48bc43eece928f005a4458c87a16ce089e8e5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 13 Mar 2023 22:49:54 -0400 Subject: [PATCH 395/479] build: ejs@3.1.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e4ed037cfa7..8a7548aff5d 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "connect-redis": "3.4.2", "cookie-parser": "1.4.6", "cookie-session": "2.0.0", - "ejs": "3.1.8", + "ejs": "3.1.9", "eslint": "8.34.0", "express-session": "1.17.2", "hbs": "4.2.0", From b8b2eff3c3eac6a1df3919a87f7c7316d40ae97a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 13 Mar 2023 22:52:34 -0400 Subject: [PATCH 396/479] build: eslint@8.36.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8a7548aff5d..b77937d439f 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "cookie-parser": "1.4.6", "cookie-session": "2.0.0", "ejs": "3.1.9", - "eslint": "8.34.0", + "eslint": "8.36.0", "express-session": "1.17.2", "hbs": "4.2.0", "marked": "0.7.0", From f540c3b0195393974d4875a410f4c00a07a2ab60 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 13 Mar 2023 22:59:15 -0400 Subject: [PATCH 397/479] build: Node.js@18.15 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b30c56c2f12..20c37976a43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.14" + node-version: "18.15" - name: Node.js 19.x node-version: "19.7" diff --git a/appveyor.yml b/appveyor.yml index ac671ef47bc..e9a6e6d57ef 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.19" - nodejs_version: "17.9" - - nodejs_version: "18.14" + - nodejs_version: "18.15" - nodejs_version: "19.7" cache: - node_modules From 3531987844e533742f1159b0c3f1e07fad2e4597 Mon Sep 17 00:00:00 2001 From: Rakesh Bisht Date: Sat, 4 Mar 2023 13:48:00 +0530 Subject: [PATCH 398/479] lint: remove unused function arguments in Route tests closes #5137 --- test/Route.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Route.js b/test/Route.js index 64dbad60ce9..2a37b9a4839 100644 --- a/test/Route.js +++ b/test/Route.js @@ -124,7 +124,7 @@ describe('Route', function(){ var req = { method: 'POST', url: '/' }; var route = new Route(''); - route.get(function(req, res, next) { + route.get(function () { throw new Error('not me!'); }) @@ -198,7 +198,7 @@ describe('Route', function(){ var req = { order: '', method: 'GET', url: '/' }; var route = new Route(''); - route.all(function(req, res, next){ + route.all(function () { throw new Error('foobar'); }); @@ -224,7 +224,7 @@ describe('Route', function(){ var req = { method: 'GET', url: '/' }; var route = new Route(''); - route.get(function(req, res, next){ + route.get(function () { throw new Error('boom!'); }); From 91b6fb83b4cf30ec626c0582f0b3a0a98d8afcb4 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Apr 2023 20:40:27 -0400 Subject: [PATCH 399/479] build: use nyc@14.1.1 for Node.js < 10 --- .github/workflows/ci.yml | 4 ++-- appveyor.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20c37976a43..856c6314e8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,11 +72,11 @@ jobs: - name: Node.js 8.x node-version: "8.17" - npm-i: mocha@7.2.0 + npm-i: mocha@7.2.0 nyc@14.1.1 - name: Node.js 9.x node-version: "9.11" - npm-i: mocha@7.2.0 + npm-i: mocha@7.2.0 nyc@14.1.1 - name: Node.js 10.x node-version: "10.24" diff --git a/appveyor.yml b/appveyor.yml index e9a6e6d57ef..88e81dfeee4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -70,12 +70,12 @@ install: # nyc for test coverage # - use 10.3.2 for Node.js < 4 # - use 11.9.0 for Node.js < 6 - # - use 14.1.1 for Node.js < 8 + # - use 14.1.1 for Node.js < 10 if ([int]$env:nodejs_version.split(".")[0] -lt 4) { npm install --silent --save-dev nyc@10.3.2 } elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) { npm install --silent --save-dev nyc@11.9.0 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) { + } elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) { npm install --silent --save-dev nyc@14.1.1 } - ps: | From 24e4a2570d15d6cca53023410f754929c5391c6f Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Apr 2023 20:43:53 -0400 Subject: [PATCH 400/479] build: Node.js@16.20 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 856c6314e8b..30767d98962 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,7 +101,7 @@ jobs: node-version: "15.14" - name: Node.js 16.x - node-version: "16.19" + node-version: "16.20" - name: Node.js 17.x node-version: "17.9" diff --git a/appveyor.yml b/appveyor.yml index 88e81dfeee4..1412811ff46 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.20" - nodejs_version: "15.14" - - nodejs_version: "16.19" + - nodejs_version: "16.20" - nodejs_version: "17.9" - nodejs_version: "18.15" - nodejs_version: "19.7" From 2a00da2067b7017f769c9100205a2a5f267a884b Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Tue, 11 Apr 2023 15:31:47 +0300 Subject: [PATCH 401/479] tests: use random port in listen test closes #5162 --- test/app.listen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/app.listen.js b/test/app.listen.js index 0eec582e69a..5b150063b9e 100644 --- a/test/app.listen.js +++ b/test/app.listen.js @@ -6,7 +6,7 @@ describe('app.listen()', function(){ it('should wrap with an HTTP server', function(done){ var app = express(); - var server = app.listen(9999, function(){ + var server = app.listen(0, function () { server.close(done) }); }) From 13df1de857688057f3c7e6d315321b19f8e4259e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 23 Aug 2023 19:49:36 -0400 Subject: [PATCH 402/479] build: eslint@8.47.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b77937d439f..fdf81725b4f 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "cookie-parser": "1.4.6", "cookie-session": "2.0.0", "ejs": "3.1.9", - "eslint": "8.36.0", + "eslint": "8.47.0", "express-session": "1.17.2", "hbs": "4.2.0", "marked": "0.7.0", From 8d8bfaac7be5d06c0a8fcc069b1ee5b0ec398cd9 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 23 Aug 2023 19:58:41 -0400 Subject: [PATCH 403/479] build: Node.js@18.17 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30767d98962..3d14976a9b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.15" + node-version: "18.17" - name: Node.js 19.x node-version: "19.7" diff --git a/appveyor.yml b/appveyor.yml index 1412811ff46..63adc423c8f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.20" - nodejs_version: "17.9" - - nodejs_version: "18.15" + - nodejs_version: "18.17" - nodejs_version: "19.7" cache: - node_modules From 02d1c3916ebaec776b4d754be54aa1500e2e9563 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 23 Aug 2023 20:07:50 -0400 Subject: [PATCH 404/479] build: Node.js@19.9 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d14976a9b2..35017785332 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,7 +110,7 @@ jobs: node-version: "18.17" - name: Node.js 19.x - node-version: "19.7" + node-version: "19.9" steps: - uses: actions/checkout@v3 diff --git a/appveyor.yml b/appveyor.yml index 63adc423c8f..44af842e858 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,7 @@ environment: - nodejs_version: "16.20" - nodejs_version: "17.9" - nodejs_version: "18.17" - - nodejs_version: "19.7" + - nodejs_version: "19.9" cache: - node_modules install: From a22920707bfd30e083e5b8e076841d226266cb06 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 1 Nov 2023 22:08:37 -0400 Subject: [PATCH 405/479] build: actions/checkout@v4 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35017785332..b545e8bd0d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,7 +113,7 @@ jobs: node-version: "19.9" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Node.js ${{ matrix.node-version }} shell: bash -eo pipefail -l {0} @@ -188,7 +188,7 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install lcov shell: bash From c4fe7de7bcb7ce241dfa7137fad96d48b75e86f3 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Fri, 16 Feb 2024 08:52:09 -0600 Subject: [PATCH 406/479] docs: update TC governance rules closes #5483 --- Contributing.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Contributing.md b/Contributing.md index 485dee597e1..89f92806328 100644 --- a/Contributing.md +++ b/Contributing.md @@ -107,7 +107,22 @@ move forward towards a consensus. It is not expected that a meeting of the TC will resolve all issues on its agenda during that meeting and may prefer to continue the discussion happening among the committers. -Members can be added to the TC at any time. Any committer can nominate another committer +Members can be added to the TC at any time. Any TC member can nominate another committer to the TC and the TC uses its standard consensus seeking process to evaluate whether or -not to add this new member. Members who do not participate consistently at the level of -a majority of the other members are expected to resign. +not to add this new member. The TC will consist of a minimum of 3 active members and a +maximum of 10. If the TC should drop below 5 members the active TC members should nominate +someone new. If a TC member is stepping down, they are encouraged (but not required) to +nominate someone to take their place. + +TC members will be added as admin's on the Github orgs, npm orgs, and other resources as +necessary to be effective in the role. + +To remain "active" a TC member should have participation within the last 6 months and miss +no more than three consecutive TC meetings. Members who do not meet this are expected to step down. +If A TC member does not step down, an issue can be opened in the discussions repo to move them +to inactive status. TC members who step down or are removed due to inactivity will be moved +into inactive status. + +Inactive status members can become active members by self nomination if the TC is not already +larger than the maximum of 10. They will also be given preference if, while at max size, an +active member steps down. From 59aae7686b995186a71da48abd4af5be72ff4ef5 Mon Sep 17 00:00:00 2001 From: Gireesh Punathil Date: Mon, 9 Mar 2020 14:12:29 +0530 Subject: [PATCH 407/479] docs: add project captains to contribution closes #4210 closes #5484 --- Contributing.md | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/Contributing.md b/Contributing.md index 89f92806328..dfe5f13833b 100644 --- a/Contributing.md +++ b/Contributing.md @@ -12,6 +12,7 @@ contributors can be involved in decision making. * A **Contributor** is any individual creating or commenting on an issue or pull request. * A **Committer** is a subset of contributors who have been given write access to the repository. +* A **Project Captain** is the lead maintainer of a repository. * A **TC (Technical Committee)** is a group of committers representing the required technical expertise to resolve rare disputes. * A **Triager** is a subset of contributors who have been given triage access to the repository. @@ -102,10 +103,10 @@ If a consensus cannot be reached that has no objections then a majority wins vot is called. It is also expected that the majority of decisions made by the TC are via a consensus seeking process and that voting is only used as a last-resort. -Resolution may involve returning the issue to committers with suggestions on how to -move forward towards a consensus. It is not expected that a meeting of the TC +Resolution may involve returning the issue to project captains with suggestions on +how to move forward towards a consensus. It is not expected that a meeting of the TC will resolve all issues on its agenda during that meeting and may prefer to continue -the discussion happening among the committers. +the discussion happening among the project captains. Members can be added to the TC at any time. Any TC member can nominate another committer to the TC and the TC uses its standard consensus seeking process to evaluate whether or @@ -126,3 +127,34 @@ into inactive status. Inactive status members can become active members by self nomination if the TC is not already larger than the maximum of 10. They will also be given preference if, while at max size, an active member steps down. + +## Project Captains + +The Express TC can designate captains for individual projects/repos in the +organizations. These captains are responsible for being the primary +day-to-day maintainers of the repo on a technical and community front. +Repo captains are empowered with repo ownership and package publication rights. +When there are conflicts, especially on topics that effect the Express project +at large, captains are responsible to raise it up to the TC and drive +those conflicts to resolution. Captains are also responsible for making sure +community members follow the community guidelines, maintaining the repo +and the published package, as well as in providing user support. + +Like TC members, Repo captains are a subset of committers. + +To become a captain for a project the candidate is expected to participate in that +project for at least 6 months as a committer prior to the request. They should have +helped with code contributions as well as triaging issues. They are also required to +have 2FA enabled on both their GitHub and npm accounts. Any TC member or existing +captain on the repo can nominate another committer to the captain role, submit a PR to +this doc, under `Current Project Captains` section (maintaining the sort order) with +the project, their GitHub handle and npm username (if different). The PR will require +at least 2 approvals from TC members and 2 weeks hold time to allow for comment and/or +dissent. When the PR is merged, a TC member will add them to the proper GitHub/npm groups. + +### Current Project Captains + +- `expressjs.com`: @crandmck +- `multer`: @LinusU +- `path-to-regexp`: @blakeembrey +- `router`: @dougwilson From 2a89eb5c749a168820d6ea96723ad8a7e979a58b Mon Sep 17 00:00:00 2001 From: christof louw Date: Thu, 27 Jul 2023 11:06:18 +0200 Subject: [PATCH 408/479] tests: fix handling multiple callbacks closes #5233 --- test/Router.js | 6 +++--- test/app.router.js | 2 +- test/app.use.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Router.js b/test/Router.js index cf5b5c1f0d3..b22001a9ff2 100644 --- a/test/Router.js +++ b/test/Router.js @@ -606,8 +606,8 @@ describe('Router', function(){ var req2 = { url: '/foo/10/bar', method: 'get' }; var router = new Router(); var sub = new Router(); + var cb = after(2, done) - done = after(2, done); sub.get('/bar', function(req, res, next) { next(); @@ -626,14 +626,14 @@ describe('Router', function(){ assert.ifError(err); assert.equal(req1.ms, 50); assert.equal(req1.originalUrl, '/foo/50/bar'); - done(); + cb() }); router.handle(req2, {}, function(err) { assert.ifError(err); assert.equal(req2.ms, 10); assert.equal(req2.originalUrl, '/foo/10/bar'); - done(); + cb() }); }); }); diff --git a/test/app.router.js b/test/app.router.js index 4fde03105c6..12b6c1fa519 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -896,7 +896,7 @@ describe('app.router', function(){ request(app) .get('/foo.json') - .expect(200, 'foo as json', done) + .expect(200, 'foo as json', cb) }) }) diff --git a/test/app.use.js b/test/app.use.js index fd9b1751a31..1de3275c8e2 100644 --- a/test/app.use.js +++ b/test/app.use.js @@ -57,7 +57,7 @@ describe('app', function(){ request(app) .get('/forum') - .expect(200, 'forum', done) + .expect(200, 'forum', cb) }) it('should set the child\'s .parent', function(){ From 3abea7f8189c73f7f219d8878343d961eb9a4910 Mon Sep 17 00:00:00 2001 From: riddlew Date: Thu, 18 May 2023 09:26:47 -0400 Subject: [PATCH 409/479] examples: remove multipart example Closes #5193 closes #5195 --- examples/README.md | 1 - examples/multipart/index.js | 62 ------------------------------------- package.json | 1 - 3 files changed, 64 deletions(-) delete mode 100644 examples/multipart/index.js diff --git a/examples/README.md b/examples/README.md index c19ed30a25c..bd1f1f6310f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -13,7 +13,6 @@ This page contains list of examples using Express. - [hello-world](./hello-world) - Simple request handler - [markdown](./markdown) - Markdown as template engine - [multi-router](./multi-router) - Working with multiple Express routers -- [multipart](./multipart) - Accepting multipart-encoded forms - [mvc](./mvc) - MVC-style controllers - [online](./online) - Tracking online user activity with `online` and `redis` packages - [params](./params) - Working with route parameters diff --git a/examples/multipart/index.js b/examples/multipart/index.js deleted file mode 100644 index ea7b86e0c94..00000000000 --- a/examples/multipart/index.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict' - -/** - * Module dependencies. - */ - -var express = require('../..'); -var multiparty = require('multiparty'); -var format = require('util').format; - -var app = module.exports = express(); - -app.get('/', function(req, res){ - res.send('' - + '

      Title:

      ' - + '

      Image:

      ' - + '

      ' - + ''); -}); - -app.post('/', function(req, res, next){ - // create a form to begin parsing - var form = new multiparty.Form(); - var image; - var title; - - form.on('error', next); - form.on('close', function(){ - res.send(format('\nuploaded %s (%d Kb) as %s' - , image.filename - , image.size / 1024 | 0 - , title)); - }); - - // listen on field event for title - form.on('field', function(name, val){ - if (name !== 'title') return; - title = val; - }); - - // listen on part event for image file - form.on('part', function(part){ - if (!part.filename) return; - if (part.name !== 'image') return part.resume(); - image = {}; - image.filename = part.filename; - image.size = 0; - part.on('data', function(buf){ - image.size += buf.length; - }); - }); - - - // parse the form - form.parse(req); -}); - -/* istanbul ignore next */ -if (!module.parent) { - app.listen(4000); - console.log('Express started on port 4000'); -} diff --git a/package.json b/package.json index fdf81725b4f..cf367736434 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,6 @@ "method-override": "3.0.0", "mocha": "10.2.0", "morgan": "1.10.0", - "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", "supertest": "6.3.0", From e720c5a21bfed5a9c73b2407797023bacad6980e Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Sat, 11 Nov 2023 20:02:47 +0100 Subject: [PATCH 410/479] docs: add documentation for benchmarks closes #5312 --- benchmarks/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 benchmarks/README.md diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 00000000000..1894c527d63 --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,34 @@ +# Express Benchmarks + +## Installation + +You will need to install [wrk](https://github.com/wg/wrk/blob/master/INSTALL) in order to run the benchmarks. + +## Running + +To run the benchmarks, first install the dependencies `npm i`, then run `make` + +The output will look something like this: + +``` + 50 connections + 1 middleware + 7.15ms + 6784.01 + + [...redacted...] + + 1000 connections + 10 middleware + 139.21ms + 6155.19 + +``` + +### Tip: Include Node.js version in output + +You can use `make && node -v` to include the node.js version in the output. + +### Tip: Save the results to a file + +You can use `make > results.log` to save the results to a file `results.log`. From 59af63ac2e6aea6a9cefb6fe27705ccf024d8373 Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Sat, 17 Feb 2024 18:26:40 +0100 Subject: [PATCH 411/479] build: Node.js@18.19 closes #5490 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b545e8bd0d1..5bef1f67edf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.17" + node-version: "18.19" - name: Node.js 19.x node-version: "19.9" diff --git a/appveyor.yml b/appveyor.yml index 44af842e858..b865082930f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.20" - nodejs_version: "17.9" - - nodejs_version: "18.17" + - nodejs_version: "18.19" - nodejs_version: "19.9" cache: - node_modules From 0e3ab6ec215fc297473323fb1e8d0df03033e774 Mon Sep 17 00:00:00 2001 From: Dmitry Kondar Date: Sat, 27 Jan 2024 20:29:09 +0200 Subject: [PATCH 412/479] examples: improve view count in cookie-sessions closes #5414 --- examples/cookie-sessions/index.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/cookie-sessions/index.js b/examples/cookie-sessions/index.js index 01c731c1c8f..83b6faee82b 100644 --- a/examples/cookie-sessions/index.js +++ b/examples/cookie-sessions/index.js @@ -13,13 +13,10 @@ var app = module.exports = express(); app.use(cookieSession({ secret: 'manny is cool' })); // do something with the session -app.use(count); - -// custom middleware -function count(req, res) { +app.get('/', function (req, res) { req.session.count = (req.session.count || 0) + 1 res.send('viewed ' + req.session.count + ' times\n') -} +}) /* istanbul ignore next */ if (!module.parent) { From 734b28190085c052e3ecd9c7d0b9595d9edb1b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Fri, 2 Feb 2024 15:35:20 +0100 Subject: [PATCH 413/479] build: support Node.js 20.x --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bef1f67edf..df123370d9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: - Node.js 17.x - Node.js 18.x - Node.js 19.x + - Node.js 20.x include: - name: Node.js 0.10 @@ -112,6 +113,8 @@ jobs: - name: Node.js 19.x node-version: "19.9" + - name: Node.js 20.x + node-version: "20.11" steps: - uses: actions/checkout@v4 From fdeb1d3176d11506557388ecaa2fe6a250e17efc Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Fri, 2 Feb 2024 16:31:12 +0100 Subject: [PATCH 414/479] build: support Node.js 20.x in appveyor closes #5429 --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index b865082930f..cb3e835a89e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,7 @@ environment: - nodejs_version: "17.9" - nodejs_version: "18.19" - nodejs_version: "19.9" + - nodejs_version: "20.11" cache: - node_modules install: From c259c3407f8c503c83d95fb1f30b132b73bb6388 Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Fri, 2 Feb 2024 15:38:07 +0100 Subject: [PATCH 415/479] build: support Node.js 21.x --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df123370d9e..e880f74dab6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: - Node.js 18.x - Node.js 19.x - Node.js 20.x + - Node.js 21.x include: - name: Node.js 0.10 @@ -115,6 +116,10 @@ jobs: - name: Node.js 20.x node-version: "20.11" + + - name: Node.js 21.x + node-version: "21.6.1" + steps: - uses: actions/checkout@v4 From b9fea1224516e372f6f63480cc1830e5f6ee63e6 Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Fri, 2 Feb 2024 16:31:54 +0100 Subject: [PATCH 416/479] build: support Node.js 21.x in appveyor --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index cb3e835a89e..5c01a8d02ad 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,6 +22,7 @@ environment: - nodejs_version: "18.19" - nodejs_version: "19.9" - nodejs_version: "20.11" + - nodejs_version: "21.6.1" cache: - node_modules install: From 23b44b3ddd45bc68487cc34cd576b117ba9d2609 Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Sat, 17 Feb 2024 18:23:09 +0100 Subject: [PATCH 417/479] build: support Node.js 21.6.2 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e880f74dab6..aa231b74d24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,7 +118,7 @@ jobs: node-version: "20.11" - name: Node.js 21.x - node-version: "21.6.1" + node-version: "21.6.2" steps: - uses: actions/checkout@v4 diff --git a/appveyor.yml b/appveyor.yml index 5c01a8d02ad..8ece802a7fc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,7 +22,7 @@ environment: - nodejs_version: "18.19" - nodejs_version: "19.9" - nodejs_version: "20.11" - - nodejs_version: "21.6.1" + - nodejs_version: "21.6.2" cache: - node_modules install: From e3eca805847e0057ab1c83e7d61a6cc1c1ca47f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Tue, 20 Feb 2024 08:44:27 +0100 Subject: [PATCH 418/479] build: pin Node 21.x to minor Co-authored-by: Aravind Nair <22199259+aravindvnair99@users.noreply.github.com> --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 8ece802a7fc..ce26523b3aa 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,7 +22,7 @@ environment: - nodejs_version: "18.19" - nodejs_version: "19.9" - nodejs_version: "20.11" - - nodejs_version: "21.6.2" + - nodejs_version: "21.6" cache: - node_modules install: From b625132864ef40b1fb119ff7c7b984573a7974c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Tue, 20 Feb 2024 08:44:34 +0100 Subject: [PATCH 419/479] build: pin Node 21.x to minor Co-authored-by: Aravind Nair <22199259+aravindvnair99@users.noreply.github.com> closes #5430 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa231b74d24..01c82f81960 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,7 +118,7 @@ jobs: node-version: "20.11" - name: Node.js 21.x - node-version: "21.6.2" + node-version: "21.6" steps: - uses: actions/checkout@v4 From 1b51edac7c5f2844e23602164a52643bb625993a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Mon, 26 Feb 2024 20:20:53 +0100 Subject: [PATCH 420/479] 4.18.3 --- History.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 9e85df3ceb2..07840455573 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,4 @@ -unreleased +4.18.3 / 2024-02-26 ========== * Fix routing requests without method diff --git a/package.json b/package.json index cf367736434..c3845d2d4bf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.18.2", + "version": "4.18.3", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 06c6b88808f6d836afc58296812235a96d708b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Sun, 10 Mar 2024 20:04:02 +0100 Subject: [PATCH 421/479] docs: update release date --- History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/History.md b/History.md index 07840455573..b674cab4b0a 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,4 @@ -4.18.3 / 2024-02-26 +4.18.3 / 2024-02-29 ========== * Fix routing requests without method From 414854b82ea4312f50641ddf7668c9194c3c209c Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Thu, 29 Feb 2024 08:49:34 -0600 Subject: [PATCH 422/479] docs: nominating @wesleytodd to be project captian --- Contributing.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Contributing.md b/Contributing.md index dfe5f13833b..7311454dd89 100644 --- a/Contributing.md +++ b/Contributing.md @@ -154,7 +154,21 @@ dissent. When the PR is merged, a TC member will add them to the proper GitHub/ ### Current Project Captains -- `expressjs.com`: @crandmck -- `multer`: @LinusU -- `path-to-regexp`: @blakeembrey -- `router`: @dougwilson +- `expressjs/express`: @wesleytodd +- `expressjs/discussions`: @wesleytodd +- `expressjs/expressjs.com`: @crandmck +- `expressjs/body-parser`: @wesleytodd +- `expressjs/multer`: @LinusU +- `expressjs/cookie-parser`: @wesleytodd +- `expressjs/generator`: @wesleytodd +- `expressjs/statusboard`: @wesleytodd +- `pillarjs/path-to-regexp`: @blakeembrey +- `pillarjs/router`: @dougwilson, @wesleytodd +- `pillarjs/finalhandler`: @wesleytodd +- `pillarjs/request`: @wesleytodd +- `jshttp/http-errors`: @wesleytodd +- `jshttp/cookie`: @wesleytodd +- `jshttp/on-finished`: @wesleytodd +- `jshttp/forwarded`: @wesleytodd +- `jshttp/proxy-addr`: @wesleytodd + From 4ee853e837dcc6c6c9f93c52278abe775c717fa1 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Wed, 28 Feb 2024 14:49:11 -0600 Subject: [PATCH 423/479] docs: loosen TC activity rules --- Contributing.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Contributing.md b/Contributing.md index 7311454dd89..a9ba84690cb 100644 --- a/Contributing.md +++ b/Contributing.md @@ -118,11 +118,13 @@ nominate someone to take their place. TC members will be added as admin's on the Github orgs, npm orgs, and other resources as necessary to be effective in the role. -To remain "active" a TC member should have participation within the last 6 months and miss -no more than three consecutive TC meetings. Members who do not meet this are expected to step down. -If A TC member does not step down, an issue can be opened in the discussions repo to move them -to inactive status. TC members who step down or are removed due to inactivity will be moved -into inactive status. +To remain "active" a TC member should have participation within the last 12 months and miss +no more than six consecutive TC meetings. Our goal is to increase participation, not punish +people for any lack of participation, this guideline should be only be used as such +(replace an inactive member with a new active one, for example). Members who do not meet this +are expected to step down. If A TC member does not step down, an issue can be opened in the +discussions repo to move them to inactive status. TC members who step down or are removed due +to inactivity will be moved into inactive status. Inactive status members can become active members by self nomination if the TC is not already larger than the maximum of 10. They will also be given preference if, while at max size, an From 69a4cf2819c4449ec6ea45649691fb43a528d5d1 Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Thu, 11 Jan 2024 11:36:11 +0000 Subject: [PATCH 424/479] deps: cookie@0.6.0 closes #5404 --- History.md | 7 +++++++ package.json | 2 +- test/res.cookie.js | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index b674cab4b0a..0559bb012c5 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * deps: cookie@0.6.0 + 4.18.3 / 2024-02-29 ========== @@ -6,6 +11,8 @@ - Fix strict json error message on Node.js 19+ - deps: content-type@~1.0.5 - deps: raw-body@2.5.2 + * deps: cookie@0.6.0 + - Add `partitioned` option 4.18.2 / 2022-10-08 =================== diff --git a/package.json b/package.json index c3845d2d4bf..990e98dc1aa 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", diff --git a/test/res.cookie.js b/test/res.cookie.js index 93deb769887..c837820605c 100644 --- a/test/res.cookie.js +++ b/test/res.cookie.js @@ -82,6 +82,22 @@ describe('res', function(){ }) }) + describe('partitioned', function () { + it('should set partitioned', function (done) { + var app = express(); + + app.use(function (req, res) { + res.cookie('name', 'tobi', { partitioned: true }); + res.end(); + }); + + request(app) + .get('/') + .expect('Set-Cookie', 'name=tobi; Path=/; Partitioned') + .expect(200, done) + }) + }) + describe('maxAge', function(){ it('should set relative expires', function(done){ var app = express(); From 567c9c665d0de4c344b8e160146050770233783c Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Sat, 16 Mar 2024 11:57:42 -0600 Subject: [PATCH 425/479] Add note on how to update docs for new release (#5541) * Update Release-Process.md Add note about updating docs. * Update Release-Process.md * Update Release-Process.md --- Release-Process.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Release-Process.md b/Release-Process.md index ae740972f77..55e62189250 100644 --- a/Release-Process.md +++ b/Release-Process.md @@ -184,3 +184,9 @@ $ npm publish **NOTE:** The version number to publish will be picked up automatically from package.json. + +### Step 7. Update documentation website + +The documentation website https://expressjs.com/ documents the current release version in various places. For a new release: +1. Change the value of `current_version` in https://github.com/expressjs/expressjs.com/blob/gh-pages/_data/express.yml to match the latest version number. +2. Add a new section to the change log. For example, for a 4.x release, https://github.com/expressjs/expressjs.com/blob/gh-pages/en/changelog/4x.md, From 0867302ddbde0e9463d0564fea5861feb708c2dd Mon Sep 17 00:00:00 2001 From: FDrag0n <34733637+FDrag0n@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:49:01 +0800 Subject: [PATCH 426/479] Prevent open redirect allow list bypass due to encodeurl Co-authored-by: Jon Church --- lib/response.js | 20 ++++++++++++++++++- test/res.location.js | 46 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/lib/response.js b/lib/response.js index fede486c06d..f7c94d10e78 100644 --- a/lib/response.js +++ b/lib/response.js @@ -34,6 +34,7 @@ var extname = path.extname; var mime = send.mime; var resolve = path.resolve; var vary = require('vary'); +var urlParse = require('url').parse; /** * Response prototype. @@ -911,8 +912,25 @@ res.location = function location(url) { loc = this.req.get('Referrer') || '/'; } + var lowerLoc = loc.toLowerCase(); + var encodedUrl = encodeUrl(loc); + if (lowerLoc.indexOf('https://') === 0 || lowerLoc.indexOf('http://') === 0) { + try { + var parsedUrl = urlParse(loc); + var parsedEncodedUrl = urlParse(encodedUrl); + // Because this can encode the host, check that we did not change the host + if (parsedUrl.host !== parsedEncodedUrl.host) { + // If the host changes after encodeUrl, return the original url + return this.set('Location', loc); + } + } catch (e) { + // If parse fails, return the original url + return this.set('Location', loc); + } + } + // set location - return this.set('Location', encodeUrl(loc)); + return this.set('Location', encodedUrl); }; /** diff --git a/test/res.location.js b/test/res.location.js index 158afac01e7..38cab027a83 100644 --- a/test/res.location.js +++ b/test/res.location.js @@ -1,13 +1,27 @@ 'use strict' var express = require('../') - , request = require('supertest'); + , request = require('supertest') + , url = require('url'); describe('res', function(){ describe('.location(url)', function(){ it('should set the header', function(done){ var app = express(); + app.use(function(req, res){ + res.location('http://google.com/').end(); + }); + + request(app) + .get('/') + .expect('Location', 'http://google.com/') + .expect(200, done) + }) + + it('should preserve trailing slashes when not present', function(done){ + var app = express(); + app.use(function(req, res){ res.location('http://google.com').end(); }); @@ -31,6 +45,36 @@ describe('res', function(){ .expect(200, done) }) + it('should not encode bad "url"', function (done) { + var app = express() + + app.use(function (req, res) { + // This is here to show a basic check one might do which + // would pass but then the location header would still be bad + if (url.parse(req.query.q).host !== 'google.com') { + res.status(400).end('Bad url'); + } + res.location(req.query.q).end(); + }); + + request(app) + .get('/?q=http://google.com\\@apple.com') + .expect(200) + .expect('Location', 'http://google.com\\@apple.com') + .end(function (err) { + if (err) { + throw err; + } + + // This ensures that our protocol check is case insensitive + request(app) + .get('/?q=HTTP://google.com\\@apple.com') + .expect(200) + .expect('Location', 'HTTP://google.com\\@apple.com') + .end(done) + }); + }); + it('should not touch already-encoded sequences in "url"', function (done) { var app = express() From 084e36506a18774f85206a65d8da04dc1107fc1b Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Wed, 20 Mar 2024 10:09:10 -0500 Subject: [PATCH 427/479] 4.19.0 --- History.md | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 0559bb012c5..877f1c56fbe 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ -unreleased +4.18.3 / 2024-03-20 ========== + * Prevent open redirect allow list bypass due to encodeurl * deps: cookie@0.6.0 4.18.3 / 2024-02-29 diff --git a/package.json b/package.json index 990e98dc1aa..eca07b57c8b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.18.3", + "version": "4.19.0", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 11f2b1db227fd42c2508c427032c1ec671b306be Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Wed, 20 Mar 2024 11:33:31 -0500 Subject: [PATCH 428/479] build: fix build due to inconsistent supertest behavior in older versions --- test/res.location.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/res.location.js b/test/res.location.js index 38cab027a83..71fbaec03d4 100644 --- a/test/res.location.js +++ b/test/res.location.js @@ -58,7 +58,7 @@ describe('res', function(){ }); request(app) - .get('/?q=http://google.com\\@apple.com') + .get('/?q=http://google.com' + encodeURIComponent('\\@apple.com')) .expect(200) .expect('Location', 'http://google.com\\@apple.com') .end(function (err) { @@ -68,7 +68,7 @@ describe('res', function(){ // This ensures that our protocol check is case insensitive request(app) - .get('/?q=HTTP://google.com\\@apple.com') + .get('/?q=HTTP://google.com' + encodeURIComponent('\\@apple.com')) .expect(200) .expect('Location', 'HTTP://google.com\\@apple.com') .end(done) From a1fa90fcea7d8e844e1c9938ad095d62669c3abd Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Wed, 20 Mar 2024 16:39:46 -0500 Subject: [PATCH 429/479] fixed un-edited version in history.md for 4.19.0 --- History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/History.md b/History.md index 877f1c56fbe..b6208ea7e32 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,4 @@ -4.18.3 / 2024-03-20 +4.19.0 / 2024-03-20 ========== * Prevent open redirect allow list bypass due to encodeurl From a003cfab034fbadb1c78ae337ee8ab389adda217 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Wed, 20 Mar 2024 16:39:46 -0500 Subject: [PATCH 430/479] Allow passing non-strings to res.location with new encoding handling checks fixes #5554 #5555 --- History.md | 5 +++++ lib/response.js | 2 +- test/res.location.js | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index b6208ea7e32..2fda95c1551 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased changes +========== + + * Allow passing non-strings to res.location with new encoding handling checks + 4.19.0 / 2024-03-20 ========== diff --git a/lib/response.js b/lib/response.js index f7c94d10e78..6fddbf35165 100644 --- a/lib/response.js +++ b/lib/response.js @@ -905,7 +905,7 @@ res.cookie = function (name, value, options) { */ res.location = function location(url) { - var loc = url; + var loc = String(url); // "back" is an alias for the referrer if (url === 'back') { diff --git a/test/res.location.js b/test/res.location.js index 71fbaec03d4..d1bbf4b6879 100644 --- a/test/res.location.js +++ b/test/res.location.js @@ -145,5 +145,20 @@ describe('res', function(){ .expect(200, done) }) }) + + if (typeof URL !== 'undefined') { + it('should accept an instance of URL', function (done) { + var app = express(); + + app.use(function(req, res){ + res.location(new URL('http://google.com/')).end(); + }); + + request(app) + .get('/') + .expect('Location', 'http://google.com/') + .expect(200, done); + }); + } }) }) From 4f0f6cc67d531431c096ea006c2191b92931bbc3 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Wed, 20 Mar 2024 17:17:59 -0500 Subject: [PATCH 431/479] 4.19.1 --- History.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 2fda95c1551..c4cdd94dacf 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,4 @@ -unreleased changes +4.19.1 / 2024-03-20 ========== * Allow passing non-strings to res.location with new encoding handling checks diff --git a/package.json b/package.json index eca07b57c8b..51c6aba2127 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.19.0", + "version": "4.19.1", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 0b746953c4bd8e377123527db11f9cd866e39f94 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Thu, 21 Mar 2024 12:13:56 -0500 Subject: [PATCH 432/479] Improved fix for open redirect allow list bypass Co-authored-by: Jon Church Co-authored-by: Blake Embrey --- History.md | 5 + lib/response.js | 31 ++--- test/res.location.js | 307 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 280 insertions(+), 63 deletions(-) diff --git a/History.md b/History.md index c4cdd94dacf..f62574a7ee1 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Improved fix for open redirect allow list bypass + 4.19.1 / 2024-03-20 ========== diff --git a/lib/response.js b/lib/response.js index 6fddbf35165..dd7b3c8201d 100644 --- a/lib/response.js +++ b/lib/response.js @@ -34,7 +34,6 @@ var extname = path.extname; var mime = send.mime; var resolve = path.resolve; var vary = require('vary'); -var urlParse = require('url').parse; /** * Response prototype. @@ -56,6 +55,7 @@ module.exports = res */ var charsetRegExp = /;\s*charset\s*=/; +var schemaAndHostRegExp = /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?\/\/[^\\\/\?]+/; /** * Set status `code`. @@ -905,32 +905,23 @@ res.cookie = function (name, value, options) { */ res.location = function location(url) { - var loc = String(url); + var loc; // "back" is an alias for the referrer if (url === 'back') { loc = this.req.get('Referrer') || '/'; + } else { + loc = String(url); } - var lowerLoc = loc.toLowerCase(); - var encodedUrl = encodeUrl(loc); - if (lowerLoc.indexOf('https://') === 0 || lowerLoc.indexOf('http://') === 0) { - try { - var parsedUrl = urlParse(loc); - var parsedEncodedUrl = urlParse(encodedUrl); - // Because this can encode the host, check that we did not change the host - if (parsedUrl.host !== parsedEncodedUrl.host) { - // If the host changes after encodeUrl, return the original url - return this.set('Location', loc); - } - } catch (e) { - // If parse fails, return the original url - return this.set('Location', loc); - } - } + var m = schemaAndHostRegExp.exec(loc); + var pos = m ? m[0].length + 1 : 0; + + // Only encode after host to avoid invalid encoding which can introduce + // vulnerabilities (e.g. `\\` to `%5C`). + loc = loc.slice(0, pos) + encodeUrl(loc.slice(pos)); - // set location - return this.set('Location', encodedUrl); + return this.set('Location', loc); }; /** diff --git a/test/res.location.js b/test/res.location.js index d1bbf4b6879..c80b38de6b8 100644 --- a/test/res.location.js +++ b/test/res.location.js @@ -2,6 +2,7 @@ var express = require('../') , request = require('supertest') + , assert = require('assert') , url = require('url'); describe('res', function(){ @@ -45,49 +46,6 @@ describe('res', function(){ .expect(200, done) }) - it('should not encode bad "url"', function (done) { - var app = express() - - app.use(function (req, res) { - // This is here to show a basic check one might do which - // would pass but then the location header would still be bad - if (url.parse(req.query.q).host !== 'google.com') { - res.status(400).end('Bad url'); - } - res.location(req.query.q).end(); - }); - - request(app) - .get('/?q=http://google.com' + encodeURIComponent('\\@apple.com')) - .expect(200) - .expect('Location', 'http://google.com\\@apple.com') - .end(function (err) { - if (err) { - throw err; - } - - // This ensures that our protocol check is case insensitive - request(app) - .get('/?q=HTTP://google.com' + encodeURIComponent('\\@apple.com')) - .expect(200) - .expect('Location', 'HTTP://google.com\\@apple.com') - .end(done) - }); - }); - - it('should not touch already-encoded sequences in "url"', function (done) { - var app = express() - - app.use(function (req, res) { - res.location('https://google.com?q=%A710').end() - }) - - request(app) - .get('/') - .expect('Location', 'https://google.com?q=%A710') - .expect(200, done) - }) - describe('when url is "back"', function () { it('should set location from "Referer" header', function (done) { var app = express() @@ -146,6 +104,79 @@ describe('res', function(){ }) }) + it('should encode data uri', function (done) { + var app = express() + app.use(function (req, res) { + res.location('data:text/javascript,export default () => { }').end(); + }); + + request(app) + .get('/') + .expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D') + .expect(200, done) + }) + + it('should encode data uri', function (done) { + var app = express() + app.use(function (req, res) { + res.location('data:text/javascript,export default () => { }').end(); + }); + + request(app) + .get('/') + .expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D') + .expect(200, done) + }) + + it('should consistently handle non-string input: boolean', function (done) { + var app = express() + app.use(function (req, res) { + res.location(true).end(); + }); + + request(app) + .get('/') + .expect('Location', 'true') + .expect(200, done) + }); + + it('should consistently handle non-string inputs: object', function (done) { + var app = express() + app.use(function (req, res) { + res.location({}).end(); + }); + + request(app) + .get('/') + .expect('Location', '[object%20Object]') + .expect(200, done) + }); + + it('should consistently handle non-string inputs: array', function (done) { + var app = express() + app.use(function (req, res) { + res.location([]).end(); + }); + + request(app) + .get('/') + .expect('Location', '') + .expect(200, done) + }); + + it('should consistently handle empty string input', function (done) { + var app = express() + app.use(function (req, res) { + res.location('').end(); + }); + + request(app) + .get('/') + .expect('Location', '') + .expect(200, done) + }); + + if (typeof URL !== 'undefined') { it('should accept an instance of URL', function (done) { var app = express(); @@ -161,4 +192,194 @@ describe('res', function(){ }); } }) + + describe('location header encoding', function() { + function createRedirectServerForDomain (domain) { + var app = express(); + app.use(function (req, res) { + var host = url.parse(req.query.q, false, true).host; + // This is here to show a basic check one might do which + // would pass but then the location header would still be bad + if (host !== domain) { + res.status(400).end('Bad host: ' + host + ' !== ' + domain); + } + res.location(req.query.q).end(); + }); + return app; + } + + function testRequestedRedirect (app, inputUrl, expected, expectedHost, done) { + return request(app) + // Encode uri because old supertest does not and is required + // to test older node versions. New supertest doesn't re-encode + // so this works in both. + .get('/?q=' + encodeURIComponent(inputUrl)) + .expect('') // No body. + .expect(200) + .expect('Location', expected) + .end(function (err, res) { + if (err) { + console.log('headers:', res.headers) + console.error('error', res.error, err); + return done(err, res); + } + + // Parse the hosts from the input URL and the Location header + var inputHost = url.parse(inputUrl, false, true).host; + var locationHost = url.parse(res.headers['location'], false, true).host; + + assert.strictEqual(locationHost, expectedHost); + + // Assert that the hosts are the same + if (inputHost !== locationHost) { + return done(new Error('Hosts do not match: ' + inputHost + " !== " + locationHost)); + } + + return done(null, res); + }); + } + + it('should not touch already-encoded sequences in "url"', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com?q=%A710', + 'https://google.com?q=%A710', + 'google.com', + done + ); + }); + + it('should consistently handle relative urls', function (done) { + var app = createRedirectServerForDomain(null); + testRequestedRedirect( + app, + '/foo/bar', + '/foo/bar', + null, + done + ); + }); + + it('should not encode urls in such a way that they can bypass redirect allow lists', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'http://google.com\\@apple.com', + 'http://google.com\\@apple.com', + 'google.com', + done + ); + }); + + it('should not be case sensitive', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'HTTP://google.com\\@apple.com', + 'HTTP://google.com\\@apple.com', + 'google.com', + done + ); + }); + + it('should work with https', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com\\@apple.com', + 'https://google.com\\@apple.com', + 'google.com', + done + ); + }); + + it('should correctly encode schemaless paths', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + '//google.com\\@apple.com/', + '//google.com\\@apple.com/', + 'google.com', + done + ); + }); + + it('should percent encode backslashes in the path', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com/foo\\bar\\baz', + 'https://google.com/foo%5Cbar%5Cbaz', + 'google.com', + done + ); + }); + + it('should encode backslashes in the path after the first backslash that triggered path parsing', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com\\@app\\l\\e.com', + 'https://google.com\\@app%5Cl%5Ce.com', + 'google.com', + done + ); + }); + + it('should escape header splitting for old node versions', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'http://google.com\\@apple.com/%0d%0afoo:%20bar', + 'http://google.com\\@apple.com/%0d%0afoo:%20bar', + 'google.com', + done + ); + }); + + it('should encode unicode correctly', function (done) { + var app = createRedirectServerForDomain(null); + testRequestedRedirect( + app, + '/%e2%98%83', + '/%e2%98%83', + null, + done + ); + }); + + it('should encode unicode correctly even with a bad host', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'http://google.com\\@apple.com/%e2%98%83', + 'http://google.com\\@apple.com/%e2%98%83', + 'google.com', + done + ); + }); + + it('should work correctly despite using deprecated url.parse', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com\'.bb.com/1.html', + 'https://google.com\'.bb.com/1.html', + 'google.com', + done + ); + }); + + it('should encode file uri path', function (done) { + var app = createRedirectServerForDomain(''); + testRequestedRedirect( + app, + 'file:///etc\\passwd', + 'file:///etc%5Cpasswd', + '', + done + ); + }); + }); }) From b28db2c12c3bd9cd763316824446f79bf81e0686 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 25 Mar 2024 09:26:03 -0500 Subject: [PATCH 433/479] 4.19.2 --- History.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index f62574a7ee1..ac2e7cf719d 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,4 @@ -unreleased +4.19.2 / 2024-03-25 ========== * Improved fix for open redirect allow list bypass diff --git a/package.json b/package.json index 51c6aba2127..f299d882b0d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.19.1", + "version": "4.19.2", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 94669f9289fdd1c640b0c35acf4d9f8e996a646c Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 25 Mar 2024 10:03:18 -0500 Subject: [PATCH 434/479] remove duplicate location test for data uri --- test/res.location.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/res.location.js b/test/res.location.js index c80b38de6b8..141ee901310 100644 --- a/test/res.location.js +++ b/test/res.location.js @@ -116,18 +116,6 @@ describe('res', function(){ .expect(200, done) }) - it('should encode data uri', function (done) { - var app = express() - app.use(function (req, res) { - res.location('data:text/javascript,export default () => { }').end(); - }); - - request(app) - .get('/') - .expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D') - .expect(200, done) - }) - it('should consistently handle non-string input: boolean', function (done) { var app = express() app.use(function (req, res) { From 51595d402ba155877e48a2a6c807b956a6d6d376 Mon Sep 17 00:00:00 2001 From: marco-ippolito Date: Tue, 26 Mar 2024 12:37:28 +0100 Subject: [PATCH 435/479] feat: document beta releases expectations --- Release-Process.md | 7 +++++++ Security.md | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/Release-Process.md b/Release-Process.md index 55e62189250..9ea0cf3212e 100644 --- a/Release-Process.md +++ b/Release-Process.md @@ -77,6 +77,13 @@ non-patch flow. - This branch contains the commits accepted so far that implement the proposal in the tracking pull request. +### Beta releases + +Beta releases are made from a proposal branch. The version number should be +incremented to the next minor version with a `-beta` suffix. For example, if the +next release is `5.0.1`, the beta release would be `5.0.1-beta.0`. The beta +release should be considered unstable and not suitable for production use. + ### Patch flow In the patch flow, simple changes are committed to the release branch which diff --git a/Security.md b/Security.md index cdcd7a6e0aa..d1c4bb7f022 100644 --- a/Security.md +++ b/Security.md @@ -29,6 +29,12 @@ announcement, and may ask for additional information or guidance. Report security bugs in third-party modules to the person or team maintaining the module. +## Beta releases + +Beta releases should be considered unstable and **not suitable for production use**. +Vulnerabilities found in beta releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section. +Due to the unstable nature of the branch it is not garanteed that the fix will be released in the next beta release. + ## Disclosure Policy When the security team receives a security bug report, they will assign it to a From 88bd6d8e3a91f2059a89493a32952c5e01a1b40a Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 26 Mar 2024 15:32:34 +0100 Subject: [PATCH 436/479] Update Release-Process.md Co-authored-by: Wes Todd --- Release-Process.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release-Process.md b/Release-Process.md index 9ea0cf3212e..0b95a6f3a7d 100644 --- a/Release-Process.md +++ b/Release-Process.md @@ -77,7 +77,7 @@ non-patch flow. - This branch contains the commits accepted so far that implement the proposal in the tracking pull request. -### Beta releases +### Pre-release Versions Beta releases are made from a proposal branch. The version number should be incremented to the next minor version with a `-beta` suffix. For example, if the From 4e3f95c0ea4ba6b3f116e65dbb5828e918ea25fb Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 26 Mar 2024 15:41:07 +0100 Subject: [PATCH 437/479] Update Security.md Co-authored-by: Wes Todd --- Security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security.md b/Security.md index d1c4bb7f022..9a9986d73ca 100644 --- a/Security.md +++ b/Security.md @@ -29,7 +29,7 @@ announcement, and may ask for additional information or guidance. Report security bugs in third-party modules to the person or team maintaining the module. -## Beta releases +## Pre-release Versions Beta releases should be considered unstable and **not suitable for production use**. Vulnerabilities found in beta releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section. From 51a76366e34a9d5ac238c48ebfbd20a020cb635e Mon Sep 17 00:00:00 2001 From: marco-ippolito Date: Tue, 26 Mar 2024 15:44:10 +0100 Subject: [PATCH 438/479] refactor: reword to pre-releases --- Release-Process.md | 8 ++++---- Security.md | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Release-Process.md b/Release-Process.md index 0b95a6f3a7d..c74f16f08b2 100644 --- a/Release-Process.md +++ b/Release-Process.md @@ -79,10 +79,10 @@ non-patch flow. ### Pre-release Versions -Beta releases are made from a proposal branch. The version number should be -incremented to the next minor version with a `-beta` suffix. For example, if the -next release is `5.0.1`, the beta release would be `5.0.1-beta.0`. The beta -release should be considered unstable and not suitable for production use. +Alpha and Beta releases are made from a proposal branch. The version number should be +incremented to the next minor version with a `-beta` or `-alpha` suffix. +For example, if the next beta release is `5.0.1`, the beta release would be `5.0.1-beta.0`. +The pre-releases should be considered unstable and not suitable for production use. ### Patch flow diff --git a/Security.md b/Security.md index 9a9986d73ca..d49ddd75a24 100644 --- a/Security.md +++ b/Security.md @@ -31,9 +31,9 @@ the module. ## Pre-release Versions -Beta releases should be considered unstable and **not suitable for production use**. -Vulnerabilities found in beta releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section. -Due to the unstable nature of the branch it is not garanteed that the fix will be released in the next beta release. +Alpha and Beta releases should be considered unstable and **not suitable for production use**. +Vulnerabilities found in pre-releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section. +Due to the unstable nature of the branch it is not garanteed that the fix will be released in the next pre-release. ## Disclosure Policy From 6d98d2e1103dfe73cb8efc47521d42c269e5bf08 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 26 Mar 2024 17:01:11 +0100 Subject: [PATCH 439/479] Update Release-Process.md Co-authored-by: Chris de Almeida --- Release-Process.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release-Process.md b/Release-Process.md index c74f16f08b2..9ca0a15ab46 100644 --- a/Release-Process.md +++ b/Release-Process.md @@ -82,7 +82,7 @@ non-patch flow. Alpha and Beta releases are made from a proposal branch. The version number should be incremented to the next minor version with a `-beta` or `-alpha` suffix. For example, if the next beta release is `5.0.1`, the beta release would be `5.0.1-beta.0`. -The pre-releases should be considered unstable and not suitable for production use. +The pre-releases are unstable and not suitable for production use. ### Patch flow From 36b814811079d8d242e69257de46576822d6bb4f Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 26 Mar 2024 17:01:17 +0100 Subject: [PATCH 440/479] Update Security.md Co-authored-by: Chris de Almeida --- Security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security.md b/Security.md index d49ddd75a24..99e8d537734 100644 --- a/Security.md +++ b/Security.md @@ -31,7 +31,7 @@ the module. ## Pre-release Versions -Alpha and Beta releases should be considered unstable and **not suitable for production use**. +Alpha and Beta releases are unstable and **not suitable for production use**. Vulnerabilities found in pre-releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section. Due to the unstable nature of the branch it is not garanteed that the fix will be released in the next pre-release. From 8b6d34963d0bf0944d7da2da6fb2b71e36a61421 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 26 Mar 2024 17:01:30 +0100 Subject: [PATCH 441/479] Update Security.md Co-authored-by: Chris de Almeida --- Security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security.md b/Security.md index 99e8d537734..39f055b3b96 100644 --- a/Security.md +++ b/Security.md @@ -33,7 +33,7 @@ the module. Alpha and Beta releases are unstable and **not suitable for production use**. Vulnerabilities found in pre-releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section. -Due to the unstable nature of the branch it is not garanteed that the fix will be released in the next pre-release. +Due to the unstable nature of the branch it is not guaranteed that any fixes will be released in the next pre-release. ## Disclosure Policy From 3ae704f67fbbea3d6ac6aeb9563b5bc48615743f Mon Sep 17 00:00:00 2001 From: Jon Church Date: Mon, 25 Mar 2024 21:43:35 -0400 Subject: [PATCH 442/479] update ci push trigger only to some branches, ignore examples, .md --- .github/workflows/ci.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01c82f81960..a16debdef9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,18 @@ name: ci on: -- pull_request -- push + push: + branches: + - master + - '4.x' + - '5.x' + paths-ignore: + - 'examples/**' + - '*.md' + pull_request: + paths-ignore: + - 'examples/**' + - '*.md' jobs: test: From 4771ba2bc3b5c6c26dd28783f0dc962d008edab8 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Mon, 25 Mar 2024 22:12:28 -0400 Subject: [PATCH 443/479] crib fastify's ci concurrency logic https://github.com/fastify/fastify/blob/76674fdf46e4da5f1c18e0c86f615c4b538282cd/.github/workflows/ci.yml#L18 --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a16debdef9a..7fc216da6ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,12 @@ on: - 'examples/**' - '*.md' +# Cancel in progress workflows +# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run +concurrency: + group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true + jobs: test: runs-on: ubuntu-latest From d546f93f2f4658c3c03828afacc6dfd26788f7a7 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Mon, 25 Mar 2024 22:29:05 -0400 Subject: [PATCH 444/479] add develop to branches --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fc216da6ce..81e48233738 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - develop - '4.x' - '5.x' paths-ignore: From 6da57c7819dfefa228ab68c884d701a8cf45f39b Mon Sep 17 00:00:00 2001 From: Jon Church Date: Mon, 25 Mar 2024 22:45:13 -0400 Subject: [PATCH 445/479] remove examples from the ignore --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81e48233738..f43deeb5385 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,11 +8,9 @@ on: - '4.x' - '5.x' paths-ignore: - - 'examples/**' - '*.md' pull_request: paths-ignore: - - 'examples/**' - '*.md' # Cancel in progress workflows From 2676a1f281e8f1cc73591d3fa8917344dcc60d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Thu, 4 Apr 2024 13:01:35 +0200 Subject: [PATCH 446/479] docs: add reference to the Threat Model * docs: add Threat Model * docs: update reference Co-authored-by: Chris de Almeida * docs: improve readability Co-authored-by: Chris de Almeida * docs: add reference to the Threat Model --------- Co-authored-by: Chris de Almeida --- Security.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Security.md b/Security.md index 39f055b3b96..6b5d2b0686e 100644 --- a/Security.md +++ b/Security.md @@ -46,6 +46,10 @@ involving the following steps: * Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible to npm. +## The Express Threat Model + +We are currently working on a new version of the security model, the most updated version can be found [here](https://github.com/expressjs/security-wg/blob/main/tools/docs/ThreatModel.md) + ## Comments on this Policy If you have suggestions on how this process could be improved please submit a From 93cf646d5c3643b0d14563795eb45aba583b1568 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Sun, 7 Apr 2024 09:40:16 -0700 Subject: [PATCH 447/479] docs: add blakeembrey as captain for encodeurl (#5579) --- Contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Contributing.md b/Contributing.md index a9ba84690cb..599df10360f 100644 --- a/Contributing.md +++ b/Contributing.md @@ -164,6 +164,7 @@ dissent. When the PR is merged, a TC member will add them to the proper GitHub/ - `expressjs/cookie-parser`: @wesleytodd - `expressjs/generator`: @wesleytodd - `expressjs/statusboard`: @wesleytodd +- `pillarjs/encodeurl`: @blakeembrey - `pillarjs/path-to-regexp`: @blakeembrey - `pillarjs/router`: @dougwilson, @wesleytodd - `pillarjs/finalhandler`: @wesleytodd @@ -173,4 +174,3 @@ dissent. When the PR is merged, a TC member will add them to the proper GitHub/ - `jshttp/on-finished`: @wesleytodd - `jshttp/forwarded`: @wesleytodd - `jshttp/proxy-addr`: @wesleytodd - From 7f9e5843b9690267cf87efe63b48d45425f9ebc3 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Mon, 8 Apr 2024 09:02:12 -0400 Subject: [PATCH 448/479] add jonchurch as repo captain on several packages --- Contributing.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Contributing.md b/Contributing.md index 599df10360f..9baa666b712 100644 --- a/Contributing.md +++ b/Contributing.md @@ -158,10 +158,12 @@ dissent. When the PR is merged, a TC member will add them to the proper GitHub/ - `expressjs/express`: @wesleytodd - `expressjs/discussions`: @wesleytodd -- `expressjs/expressjs.com`: @crandmck -- `expressjs/body-parser`: @wesleytodd +- `expressjs/expressjs.com`: @crandmck, @jonchurch +- `expressjs/body-parser`: @wesleytodd, @jonchurch - `expressjs/multer`: @LinusU +- `expressjs/morgan`: @jonchurch - `expressjs/cookie-parser`: @wesleytodd +- `expressjs/cors`: @jonchurch - `expressjs/generator`: @wesleytodd - `expressjs/statusboard`: @wesleytodd - `pillarjs/encodeurl`: @blakeembrey @@ -169,7 +171,7 @@ dissent. When the PR is merged, a TC member will add them to the proper GitHub/ - `pillarjs/router`: @dougwilson, @wesleytodd - `pillarjs/finalhandler`: @wesleytodd - `pillarjs/request`: @wesleytodd -- `jshttp/http-errors`: @wesleytodd +- `jshttp/http-errors`: @wesleytodd, @jonchurch - `jshttp/cookie`: @wesleytodd - `jshttp/on-finished`: @wesleytodd - `jshttp/forwarded`: @wesleytodd From 815f799310a5627c000d4a5156c1c958e4947b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Marqu=C3=ADnez=20Prado?= <25435858+inigomarquinez@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:53:52 +0200 Subject: [PATCH 449/479] docs: update reference to the threat model (#5590) PR: https://github.com/expressjs/express/pull/5590 --- Security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security.md b/Security.md index 6b5d2b0686e..dcfbe88abd4 100644 --- a/Security.md +++ b/Security.md @@ -48,7 +48,7 @@ involving the following steps: ## The Express Threat Model -We are currently working on a new version of the security model, the most updated version can be found [here](https://github.com/expressjs/security-wg/blob/main/tools/docs/ThreatModel.md) +We are currently working on a new version of the security model, the most updated version can be found [here](https://github.com/expressjs/security-wg/blob/main/docs/ThreatModel.md) ## Comments on this Policy From 6abec204c0a6dc0b2a7adcd6f118ca5fb757e4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Wed, 17 Apr 2024 13:53:16 +0200 Subject: [PATCH 450/479] docs: update triage nomination policy (#5600) PR-URL: https://github.com/expressjs/express/pull/5600 --- Contributing.md | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/Contributing.md b/Contributing.md index 9baa666b712..f2ffe4935a2 100644 --- a/Contributing.md +++ b/Contributing.md @@ -63,29 +63,14 @@ compromise among committers be the default resolution mechanism. Anyone can become a triager! Read more about the process of being a triager in [the triage process document](Triager-Guide.md). -[Open an issue in `expressjs/express` repo](https://github.com/expressjs/express/issues/new) -to request the triage role. State that you have read and agree to the -[Code of Conduct](Code-Of-Conduct.md) and details of the role. - -Here is an example issue content you can copy and paste: - -``` -Title: Request triager role for - -I have read and understood the project's Code of Conduct. -I also have read and understood the process and best practices around Express triaging. - -I request for a triager role for the following GitHub organizations: - -jshttp -pillarjs -express -``` - -Once you have opened your issue, a member of the TC will add you to the `triage` team in -the organizations requested. They will then close the issue. - -Happy triaging! +Currently, any existing [organization member](https://github.com/orgs/expressjs/people) can nominate +a new triager. If you are interested in becoming a triager, our best advice is to actively participate +in the community by helping triaging issues and pull requests. As well we recommend +to engage in other community activities like attending the TC meetings, and participating in the Slack +discussions. + +You can also reach out to any of the [organization members](https://github.com/orgs/expressjs/people) +if you have questions or need guidance. ## Becoming a Committer From 26e53f0fbcaf1fa71a68a42dcd6c17af05fe6ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Wed, 17 Apr 2024 15:13:07 +0200 Subject: [PATCH 451/479] ci: add CodeQL (SAST) (#5433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/expressjs/express/pull/5433 --------- Co-authored-by: Íñigo Marquínez Prado <25435858+inigomarquinez@users.noreply.github.com> --- .github/workflows/codeql.yml | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..db4e01aff56 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,66 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: ["master"] + pull_request: + # The branches below must be a subset of the branches above + branches: ["master"] + schedule: + - cron: "0 0 * * 1" + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 + with: + languages: javascript + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + # - name: Autobuild + # uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 + with: + category: "/language:javascript" From d97d79ed9a25099ec4f0537ad8bf2a9378350a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Wed, 17 Apr 2024 14:25:38 +0200 Subject: [PATCH 452/479] docs: add UlisesGascon as triage initiative captain --- Contributing.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Contributing.md b/Contributing.md index f2ffe4935a2..4262b0f20db 100644 --- a/Contributing.md +++ b/Contributing.md @@ -161,3 +161,7 @@ dissent. When the PR is merged, a TC member will add them to the proper GitHub/ - `jshttp/on-finished`: @wesleytodd - `jshttp/forwarded`: @wesleytodd - `jshttp/proxy-addr`: @wesleytodd + +### Current Initiative Captains + +- Triage team [ref](https://github.com/expressjs/discussions/issues/227): @UlisesGascon From bf91946bd406b0c6f045fe81331de1725e9cee43 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Sat, 4 May 2024 13:53:09 -0700 Subject: [PATCH 453/479] deps: encodeurl@~2.0.0 (#5569) --- History.md | 6 ++++++ lib/response.js | 10 +--------- package.json | 2 +- test/res.location.js | 17 +++-------------- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/History.md b/History.md index ac2e7cf719d..1aefd4b9686 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,9 @@ +unreleased +========== + + * deps: encodeurl@~2.0.0 + - Removes encoding of `\`, `|`, and `^` to align better with URL spec + 4.19.2 / 2024-03-25 ========== diff --git a/lib/response.js b/lib/response.js index dd7b3c8201d..29845a7d830 100644 --- a/lib/response.js +++ b/lib/response.js @@ -55,7 +55,6 @@ module.exports = res */ var charsetRegExp = /;\s*charset\s*=/; -var schemaAndHostRegExp = /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?\/\/[^\\\/\?]+/; /** * Set status `code`. @@ -914,14 +913,7 @@ res.location = function location(url) { loc = String(url); } - var m = schemaAndHostRegExp.exec(loc); - var pos = m ? m[0].length + 1 : 0; - - // Only encode after host to avoid invalid encoding which can introduce - // vulnerabilities (e.g. `\\` to `%5C`). - loc = loc.slice(0, pos) + encodeUrl(loc.slice(pos)); - - return this.set('Location', loc); + return this.set('Location', encodeUrl(loc)); }; /** diff --git a/package.json b/package.json index f299d882b0d..88e4206fe67 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.2.0", diff --git a/test/res.location.js b/test/res.location.js index 141ee901310..2e88002625f 100644 --- a/test/res.location.js +++ b/test/res.location.js @@ -293,23 +293,12 @@ describe('res', function(){ ); }); - it('should percent encode backslashes in the path', function (done) { + it('should keep backslashes in the path', function (done) { var app = createRedirectServerForDomain('google.com'); testRequestedRedirect( app, 'https://google.com/foo\\bar\\baz', - 'https://google.com/foo%5Cbar%5Cbaz', - 'google.com', - done - ); - }); - - it('should encode backslashes in the path after the first backslash that triggered path parsing', function (done) { - var app = createRedirectServerForDomain('google.com'); - testRequestedRedirect( - app, - 'https://google.com\\@app\\l\\e.com', - 'https://google.com\\@app%5Cl%5Ce.com', + 'https://google.com/foo\\bar\\baz', 'google.com', done ); @@ -364,7 +353,7 @@ describe('res', function(){ testRequestedRedirect( app, 'file:///etc\\passwd', - 'file:///etc%5Cpasswd', + 'file:///etc\\passwd', '', done ); From 8417c60fcfd7a9523e8783fd3f489d771df1ce44 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Sat, 4 May 2024 17:09:52 -0400 Subject: [PATCH 454/479] skip QUERY method test (#5628) --- test/app.router.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/app.router.js b/test/app.router.js index 12b6c1fa519..d9ddc69afc5 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -35,8 +35,25 @@ describe('app.router', function(){ }) describe('methods', function(){ + function getMajorVersion(versionString) { + return versionString.split('.')[0]; + } + + function shouldSkipQuery(versionString) { + // Temporarily skipping this test on 21 and 22 + // update this implementation to run on those release lines on supported versions once they exist + // upstream tracking https://github.com/nodejs/node/pull/51719 + // express tracking issue: https://github.com/expressjs/express/issues/5615 + var majorsToSkip = { + "21": true, + "22": true + } + return majorsToSkip[getMajorVersion(versionString)] + } + methods.concat('del').forEach(function(method){ if (method === 'connect') return; + if (method === 'query' && shouldSkipQuery(process.versions.node)) return it('should include ' + method.toUpperCase(), function(done){ var app = express(); From b44191eb3de5fcc94b384d7d6028b8c9a0144231 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Sat, 4 May 2024 18:01:42 -0400 Subject: [PATCH 455/479] ignore ETAG query test as well, reuse skip util (#5639) --- test/app.router.js | 18 ++---------------- test/res.send.js | 3 +++ test/support/utils.js | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/test/app.router.js b/test/app.router.js index d9ddc69afc5..ae87092f00f 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -6,6 +6,8 @@ var express = require('../') , assert = require('assert') , methods = require('methods'); +var shouldSkipQuery = require('./support/utils').shouldSkipQuery + describe('app.router', function(){ it('should restore req.params after leaving router', function(done){ var app = express(); @@ -35,22 +37,6 @@ describe('app.router', function(){ }) describe('methods', function(){ - function getMajorVersion(versionString) { - return versionString.split('.')[0]; - } - - function shouldSkipQuery(versionString) { - // Temporarily skipping this test on 21 and 22 - // update this implementation to run on those release lines on supported versions once they exist - // upstream tracking https://github.com/nodejs/node/pull/51719 - // express tracking issue: https://github.com/expressjs/express/issues/5615 - var majorsToSkip = { - "21": true, - "22": true - } - return majorsToSkip[getMajorVersion(versionString)] - } - methods.concat('del').forEach(function(method){ if (method === 'connect') return; if (method === 'query' && shouldSkipQuery(process.versions.node)) return diff --git a/test/res.send.js b/test/res.send.js index c92568db6ad..1e1835f8238 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -7,6 +7,8 @@ var methods = require('methods'); var request = require('supertest'); var utils = require('./support/utils'); +var shouldSkipQuery = require('./support/utils').shouldSkipQuery + describe('res', function(){ describe('.send()', function(){ it('should set body to ""', function(done){ @@ -407,6 +409,7 @@ describe('res', function(){ methods.forEach(function (method) { if (method === 'connect') return; + if (method === 'query' && shouldSkipQuery(process.versions.node)) return it('should send ETag in response to ' + method.toUpperCase() + ' request', function (done) { var app = express(); diff --git a/test/support/utils.js b/test/support/utils.js index 5b4062e0b2a..440a0269bc1 100644 --- a/test/support/utils.js +++ b/test/support/utils.js @@ -16,6 +16,7 @@ exports.shouldHaveBody = shouldHaveBody exports.shouldHaveHeader = shouldHaveHeader exports.shouldNotHaveBody = shouldNotHaveBody exports.shouldNotHaveHeader = shouldNotHaveHeader; +exports.shouldSkipQuery = shouldSkipQuery /** * Assert that a supertest response has a specific body. @@ -70,3 +71,20 @@ function shouldNotHaveHeader(header) { assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header); }; } + +function getMajorVersion(versionString) { + return versionString.split('.')[0]; +} + +function shouldSkipQuery(versionString) { + // Temporarily skipping this test on 21 and 22 + // update this implementation to run on those release lines on supported versions once they exist + // upstream tracking https://github.com/nodejs/node/pull/51719 + // express tracking issue: https://github.com/expressjs/express/issues/5615 + var majorsToSkip = { + "21": true, + "22": true + } + return majorsToSkip[getMajorVersion(versionString)] +} + From 4b9cd2fd0e13519a16bc36e8b4212e7924698b2e Mon Sep 17 00:00:00 2001 From: Mert Can Altin Date: Sun, 5 May 2024 01:15:53 +0300 Subject: [PATCH 456/479] add support Node.js@22 in the CI (#5627) Co-authored-by: Mert Can Altin --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f43deeb5385..488d394f622 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: - Node.js 19.x - Node.js 20.x - Node.js 21.x + - Node.js 22.x include: - name: Node.js 0.10 @@ -134,6 +135,9 @@ jobs: - name: Node.js 21.x node-version: "21.6" + + - name: Node.js 22.x + node-version: "22.0" steps: - uses: actions/checkout@v4 diff --git a/appveyor.yml b/appveyor.yml index ce26523b3aa..629499bf37c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -23,6 +23,7 @@ environment: - nodejs_version: "19.9" - nodejs_version: "20.11" - nodejs_version: "21.6" + - nodejs_version: "22.0" cache: - node_modules install: From 700349ffaf6140195a2d5f8173dd732c90c5aacc Mon Sep 17 00:00:00 2001 From: Mert Can Altin Date: Thu, 9 May 2024 00:02:11 +0300 Subject: [PATCH 457/479] doc: add table of contents, tc/triager lists to readme (#5619) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * doc: updated readme file * doc: updated readme file for doc lint * Update Readme.md Co-authored-by: krzysdz * Update Readme.md Co-authored-by: Ulises Gascón * Update Readme.md Co-authored-by: Ulises Gascón * Update Readme.md Co-authored-by: Christine Belzie <105683440+CBID2@users.noreply.github.com> * repair readme * added Emeritus area * Add @carpasse to the triager team * removed old collaborators * add missing triagers * lint * Update Readme.md Co-authored-by: Jon Church * Update Readme.md Co-authored-by: Jon Church * dedent to fix ToC spacing * fixup! dedent to fix ToC spacing * us @ for jonchurch * format names to use github handles first, single line * added emeritus triagers * edited title * added emeritus team members * added menu head * edited emeritus * Update Readme.md Co-authored-by: Jon Church * Update Readme.md Co-authored-by: Jon Church * edits to TC and anchors * Update Readme.md Co-authored-by: Jon Church * Update Readme.md Co-authored-by: Jon Church * Update Readme.md Co-authored-by: Jon Church * Update Readme.md Co-authored-by: Jon Church * Update Readme.md Co-authored-by: Jon Church * Update Readme.md Co-authored-by: Ulises Gascón * Update Readme.md Co-authored-by: Ulises Gascón * Update Readme.md Co-authored-by: Ulises Gascón * Update Readme.md Co-authored-by: Ulises Gascón * Update Readme.md Co-authored-by: Ulises Gascón --------- Co-authored-by: Mert Can Altin Co-authored-by: krzysdz Co-authored-by: Ulises Gascón Co-authored-by: Christine Belzie <105683440+CBID2@users.noreply.github.com> Co-authored-by: Jon Church --- Readme.md | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index d0f3cf56e6d..01acf9e2f19 100644 --- a/Readme.md +++ b/Readme.md @@ -1,10 +1,27 @@ [![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/) - Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org). +**Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org).** - [![NPM Version][npm-version-image]][npm-url] - [![NPM Install Size][npm-install-size-image]][npm-install-size-url] - [![NPM Downloads][npm-downloads-image]][npm-downloads-url] +**This project has a [Code of Conduct][].** + +## Table of contents + +* [Installation](#Installation) +* [Features](#Features) +* [Docs & Community](#docs--community) +* [Quick Start](#Quick-Start) +* [Running Tests](#Running-Tests) +* [Philosophy](#Philosophy) +* [Examples](#Examples) +* [Contributing to Express](#Contributing) +* [TC (Technical Committee)](#tc-technical-committee) +* [Triagers](#triagers) +* [License](#license) + + +[![NPM Version][npm-version-image]][npm-url] +[![NPM Install Size][npm-install-size-image]][npm-install-size-url] +[![NPM Downloads][npm-downloads-image]][npm-downloads-url] ```js const express = require('express') @@ -148,6 +165,78 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d [List of all contributors](https://github.com/expressjs/express/graphs/contributors) +### TC (Technical Committee) + +* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him) +* [jonchurch](https://github.com/jonchurch) - **Jon Church** +* [wesleytodd](https://github.com/wesleytodd) - **Wes Todd** +* [LinusU](https://github.com/LinusU) - **Linus Unnebäck** +* [blakeembrey](https://github.com/blakeembrey) - **Blake Embrey** +* [sheplu](https://github.com/sheplu) - **Jean Burellier** +* [crandmck](https://github.com/crandmck) - **Rand McKinney** + +
      +TC emeriti members + +#### TC emeriti members + + * [dougwilson](https://github.com/dougwilson) - **Douglas Wilson** + * [hacksparrow](https://github.com/hacksparrow) - **Hage Yaapa** + * [jonathanong](https://github.com/jonathanong) - **jongleberry** + * [niftylettuce](https://github.com/niftylettuce) - **niftylettuce** + * [troygoode](https://github.com/troygoode) - **Troy Goode** +
      + + +### Triagers + +* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair** +* [carpasse](https://github.com/carpasse) - **Carlos Serrano** +* [CBID2](https://github.com/CBID2) - **Christine Belzie** +* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim** +* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him) +* [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin** +* [0ss](https://github.com/0ss) - **Salah** +* [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him) +* [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi** +* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him) +* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego** +* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger** + +
      +Triagers emeriti members + +#### Emeritus Triagers + + * [AuggieH](https://github.com/AuggieH) - **Auggie Hudak** + * [G-Rath](https://github.com/G-Rath) - **Gareth Jones** + * [MohammadXroid](https://github.com/MohammadXroid) - **Mohammad Ayashi** + * [NawafSwe](https://github.com/NawafSwe) - **Nawaf Alsharqi** + * [NotMoni](https://github.com/NotMoni) - **Moni** + * [VigneshMurugan](https://github.com/VigneshMurugan) - **Vignesh Murugan** + * [davidmashe](https://github.com/davidmashe) - **David Ashe** + * [digitaIfabric](https://github.com/digitaIfabric) - **David** + * [e-l-i-s-e](https://github.com/e-l-i-s-e) - **Elise Bonner** + * [fed135](https://github.com/fed135) - **Frederic Charette** + * [firmanJS](https://github.com/firmanJS) - **Firman Abdul Hakim** + * [getspooky](https://github.com/getspooky) - **Yasser Ameur** + * [ghinks](https://github.com/ghinks) - **Glenn** + * [ghousemohamed](https://github.com/ghousemohamed) - **Ghouse Mohamed** + * [gireeshpunathil](https://github.com/gireeshpunathil) - **Gireesh Punathil** + * [jake32321](https://github.com/jake32321) - **Jake Reed** + * [jonchurch](https://github.com/jonchurch) - **Jon Church** + * [lekanikotun](https://github.com/lekanikotun) - **Troy Goode** + * [marsonya](https://github.com/marsonya) - **Lekan Ikotun** + * [mastermatt](https://github.com/mastermatt) - **Matt R. Wilson** + * [maxakuru](https://github.com/maxakuru) - **Max Edell** + * [mlrawlings](https://github.com/mlrawlings) - **Michael Rawlings** + * [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov** + * [sheplu](https://github.com/sheplu) - **Jean Burellier** + * [tarunyadav1](https://github.com/tarunyadav1) - **Tarun yadav** + * [tunniclm](https://github.com/tunniclm) - **Mike Tunnicliffe** +
      + + ## License [MIT](LICENSE) @@ -164,3 +253,4 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d [npm-install-size-url]: https://packagephobia.com/result?p=express [npm-url]: https://npmjs.org/package/express [npm-version-image]: https://badgen.net/npm/v/express +[Code of Conduct]: https://github.com/expressjs/express/blob/master/Code-Of-Conduct.md From 897290b68549034fb12f7bbb2cf6dae6c6f36096 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Fri, 10 May 2024 16:09:39 -0700 Subject: [PATCH 458/479] List and sort all projects, add captains --- Contributing.md | 76 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/Contributing.md b/Contributing.md index 4262b0f20db..46289af6137 100644 --- a/Contributing.md +++ b/Contributing.md @@ -63,13 +63,13 @@ compromise among committers be the default resolution mechanism. Anyone can become a triager! Read more about the process of being a triager in [the triage process document](Triager-Guide.md). -Currently, any existing [organization member](https://github.com/orgs/expressjs/people) can nominate -a new triager. If you are interested in becoming a triager, our best advice is to actively participate +Currently, any existing [organization member](https://github.com/orgs/expressjs/people) can nominate +a new triager. If you are interested in becoming a triager, our best advice is to actively participate in the community by helping triaging issues and pull requests. As well we recommend -to engage in other community activities like attending the TC meetings, and participating in the Slack +to engage in other community activities like attending the TC meetings, and participating in the Slack discussions. -You can also reach out to any of the [organization members](https://github.com/orgs/expressjs/people) +You can also reach out to any of the [organization members](https://github.com/orgs/expressjs/people) if you have questions or need guidance. ## Becoming a Committer @@ -139,28 +139,70 @@ the project, their GitHub handle and npm username (if different). The PR will re at least 2 approvals from TC members and 2 weeks hold time to allow for comment and/or dissent. When the PR is merged, a TC member will add them to the proper GitHub/npm groups. -### Current Project Captains +### Active Projects and Captains -- `expressjs/express`: @wesleytodd -- `expressjs/discussions`: @wesleytodd -- `expressjs/expressjs.com`: @crandmck, @jonchurch +- `expressjs/badgeboard`: @wesleytodd +- `expressjs/basic-auth-connect`: N/A - `expressjs/body-parser`: @wesleytodd, @jonchurch -- `expressjs/multer`: @LinusU -- `expressjs/morgan`: @jonchurch +- `expressjs/compression`: N/A +- `expressjs/connect-multiparty`: N/A - `expressjs/cookie-parser`: @wesleytodd +- `expressjs/cookie-session`: N/A - `expressjs/cors`: @jonchurch +- `expressjs/discussions`: @wesleytodd +- `expressjs/errorhandler`: N/A +- `expressjs/express-paginate`: N/A +- `expressjs/express`: @wesleytodd +- `expressjs/expressjs.com`: @crandmck, @jonchurch +- `expressjs/flash`: N/A - `expressjs/generator`: @wesleytodd +- `expressjs/method-override`: N/A +- `expressjs/morgan`: @jonchurch +- `expressjs/multer`: @LinusU +- `expressjs/response-time`: @blakeembrey +- `expressjs/serve-favicon`: N/A +- `expressjs/serve-index`: N/A +- `expressjs/serve-static`: N/A +- `expressjs/session`: N/A - `expressjs/statusboard`: @wesleytodd -- `pillarjs/encodeurl`: @blakeembrey -- `pillarjs/path-to-regexp`: @blakeembrey -- `pillarjs/router`: @dougwilson, @wesleytodd -- `pillarjs/finalhandler`: @wesleytodd -- `pillarjs/request`: @wesleytodd -- `jshttp/http-errors`: @wesleytodd, @jonchurch +- `expressjs/timeout`: N/A +- `expressjs/vhost`: N/A +- `jshttp/accepts`: @blakeembrey +- `jshttp/basic-auth`: @blakeembrey +- `jshttp/compressible`: @blakeembrey +- `jshttp/content-disposition`: @blakeembrey +- `jshttp/content-type`: @blakeembrey - `jshttp/cookie`: @wesleytodd +- `jshttp/etag`: @blakeembrey +- `jshttp/forwarded`: @blakeembrey +- `jshttp/fresh`: @blakeembrey +- `jshttp/http-assert`: @wesleytodd, @jonchurch +- `jshttp/http-errors`: @wesleytodd, @jonchurch +- `jshttp/media-typer`: @blakeembrey +- `jshttp/methods`: @blakeembrey +- `jshttp/mime-db`: @blakeembrey +- `jshttp/mime-types`: @blakeembrey +- `jshttp/negotiator`: @blakeembrey - `jshttp/on-finished`: @wesleytodd -- `jshttp/forwarded`: @wesleytodd +- `jshttp/on-headers`: @blakeembrey - `jshttp/proxy-addr`: @wesleytodd +- `jshttp/range-parser`: @blakeembrey +- `jshttp/statuses`: @blakeembrey +- `jshttp/type-is`: @blakeembrey +- `jshttp/vary`: @blakeembrey +- `pillarjs/cookies`: @blakeembrey +- `pillarjs/csrf`: N/A +- `pillarjs/encodeurl`: @blakeembrey +- `pillarjs/finalhandler`: @wesleytodd +- `pillarjs/hbs`: N/A +- `pillarjs/multiparty`: @blakeembrey +- `pillarjs/parseurl`: @blakeembrey +- `pillarjs/path-to-regexp`: @blakeembrey +- `pillarjs/request`: @wesleytodd +- `pillarjs/resolve-path`: @blakeembrey +- `pillarjs/router`: @blakeembrey +- `pillarjs/send`: @blakeembrey +- `pillarjs/understanding-csrf`: N/A ### Current Initiative Captains From a7d6d29ed3a8eeb91954447696d1a28b982702a4 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Mon, 13 May 2024 16:12:36 -0700 Subject: [PATCH 459/479] Add @UlisesGascon to mime repos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ulises Gascón --- Contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Contributing.md b/Contributing.md index 46289af6137..292fdc40b71 100644 --- a/Contributing.md +++ b/Contributing.md @@ -180,8 +180,8 @@ dissent. When the PR is merged, a TC member will add them to the proper GitHub/ - `jshttp/http-errors`: @wesleytodd, @jonchurch - `jshttp/media-typer`: @blakeembrey - `jshttp/methods`: @blakeembrey -- `jshttp/mime-db`: @blakeembrey -- `jshttp/mime-types`: @blakeembrey +- `jshttp/mime-db`: @blakeembrey, @UlisesGascon +- `jshttp/mime-types`: @blakeembrey, @UlisesGascon - `jshttp/negotiator`: @blakeembrey - `jshttp/on-finished`: @wesleytodd - `jshttp/on-headers`: @blakeembrey From 2803a2b35ae37209a44a8d3b19c141482fd57437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Thu, 23 May 2024 00:29:16 +0200 Subject: [PATCH 460/479] docs: add @UlisesGascon as captain for cookie-parser (#5666) --- Contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Contributing.md b/Contributing.md index 292fdc40b71..1654cee02f2 100644 --- a/Contributing.md +++ b/Contributing.md @@ -146,7 +146,7 @@ dissent. When the PR is merged, a TC member will add them to the proper GitHub/ - `expressjs/body-parser`: @wesleytodd, @jonchurch - `expressjs/compression`: N/A - `expressjs/connect-multiparty`: N/A -- `expressjs/cookie-parser`: @wesleytodd +- `expressjs/cookie-parser`: @wesleytodd, @UlisesGascon - `expressjs/cookie-session`: N/A - `expressjs/cors`: @jonchurch - `expressjs/discussions`: @wesleytodd From 689073d657b646b5d01448a6a69f88016f40761b Mon Sep 17 00:00:00 2001 From: Chris de Almeida Date: Wed, 5 Jun 2024 16:25:58 -0500 Subject: [PATCH 461/479] =?UTF-8?q?=E2=9C=A8=20bring=20back=20query=20test?= =?UTF-8?q?s=20for=20node=2021=20(#5690)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 11 +---------- .npmrc | 1 + test/support/utils.js | 3 +-- 3 files changed, 3 insertions(+), 12 deletions(-) create mode 100644 .npmrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 488d394f622..02137e595ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,7 +135,7 @@ jobs: - name: Node.js 21.x node-version: "21.6" - + - name: Node.js 22.x node-version: "22.0" @@ -148,15 +148,6 @@ jobs: nvm install --default ${{ matrix.node-version }} dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" - - name: Configure npm - run: | - npm config set loglevel error - if [[ "$(npm config get package-lock)" == "true" ]]; then - npm config set package-lock false - else - 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 != '' diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000000..43c97e719a5 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/test/support/utils.js b/test/support/utils.js index 440a0269bc1..a4d9fb8b548 100644 --- a/test/support/utils.js +++ b/test/support/utils.js @@ -77,12 +77,11 @@ function getMajorVersion(versionString) { } function shouldSkipQuery(versionString) { - // Temporarily skipping this test on 21 and 22 + // Temporarily skipping this test on 22 // update this implementation to run on those release lines on supported versions once they exist // upstream tracking https://github.com/nodejs/node/pull/51719 // express tracking issue: https://github.com/expressjs/express/issues/5615 var majorsToSkip = { - "21": true, "22": true } return majorsToSkip[getMajorVersion(versionString)] From f42b160bbc0c391c06cad1c6c37eea5305f78cd2 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Fri, 7 Jun 2024 19:48:48 -0400 Subject: [PATCH 462/479] [v4] Deprecate `res.clearCookie` accepting `options.maxAge` and `options.expires` (#5672) * add deprecation notice for res.clearCookie maxAge/expires * update History.md for clearCookie deprecation change * add tests to codify deprecated behavior Co-authored-by: Chris de Almeida --------- Co-authored-by: Chris de Almeida --- History.md | 2 ++ lib/response.js | 8 ++++++++ test/res.clearCookie.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/History.md b/History.md index 1aefd4b9686..c02b24ffba2 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,8 @@ unreleased * deps: encodeurl@~2.0.0 - Removes encoding of `\`, `|`, and `^` to align better with URL spec + * Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie` + - Will be ignored in v5, clearCookie will set a cookie with an expires in the past to instruct clients to delete the cookie 4.19.2 / 2024-03-25 ========== diff --git a/lib/response.js b/lib/response.js index 29845a7d830..68d969ff05b 100644 --- a/lib/response.js +++ b/lib/response.js @@ -822,6 +822,14 @@ res.get = function(field){ */ res.clearCookie = function clearCookie(name, options) { + if (options) { + if (options.maxAge) { + deprecate('res.clearCookie: Passing "options.maxAge" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.'); + } + if (options.expires) { + deprecate('res.clearCookie: Passing "options.expires" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.'); + } + } var opts = merge({ expires: new Date(1), path: '/' }, options); return this.cookie(name, '', opts); diff --git a/test/res.clearCookie.js b/test/res.clearCookie.js index fc0cfb99a3d..3d8a6a5a81f 100644 --- a/test/res.clearCookie.js +++ b/test/res.clearCookie.js @@ -32,5 +32,37 @@ describe('res', function(){ .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') .expect(200, done) }) + + it('should set expires when passed', function(done) { + var expiresAt = new Date() + var app = express(); + + app.use(function(req, res){ + res.clearCookie('sid', { expires: expiresAt }).end(); + }); + + request(app) + .get('/') + .expect('Set-Cookie', 'sid=; Path=/; Expires=' + expiresAt.toUTCString() ) + .expect(200, done) + }) + + it('should set both maxAge and expires when passed', function(done) { + var maxAgeInMs = 10000 + var expiresAt = new Date() + var expectedExpires = new Date(expiresAt.getTime() + maxAgeInMs) + var app = express(); + + app.use(function(req, res){ + res.clearCookie('sid', { expires: expiresAt, maxAge: maxAgeInMs }).end(); + }); + + request(app) + .get('/') + // yes, this is the behavior. When we set a max-age, we also set expires to a date 10 sec ahead of expires + // even if we set max-age only, we will also set an expires 10 sec in the future + .expect('Set-Cookie', 'sid=; Max-Age=10; Path=/; Expires=' + expectedExpires.toUTCString()) + .expect(200, done) + }) }) }) From 61421a8c0c2abf011868d90df93813992e3c7563 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Sat, 8 Jun 2024 23:25:42 -0400 Subject: [PATCH 463/479] skip QUERY tests for Node 21 only, still not supported (#5695) * skip QUERY tests for Node 21 only, still not supported QUERY support has now landed in Node 22.2.0, but is still not supported in 21.7.3 QUERY showed up in http.METHODS in 21.7.2. Only Node versions after that will attempt to run tests for it, based on the way we dynamically test members of the http.METHODS array from Node * update CI to run on 21.7 and 22.2 --- .github/workflows/ci.yml | 4 ++-- test/app.router.js | 4 +++- test/res.send.js | 4 +++- test/support/utils.js | 11 ++++------- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02137e595ec..920db416d61 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -134,10 +134,10 @@ jobs: node-version: "20.11" - name: Node.js 21.x - node-version: "21.6" + node-version: "21.7" - name: Node.js 22.x - node-version: "22.0" + node-version: "22.2" steps: - uses: actions/checkout@v4 diff --git a/test/app.router.js b/test/app.router.js index ae87092f00f..707333f0432 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -39,9 +39,11 @@ describe('app.router', function(){ describe('methods', function(){ methods.concat('del').forEach(function(method){ if (method === 'connect') return; - if (method === 'query' && shouldSkipQuery(process.versions.node)) return it('should include ' + method.toUpperCase(), function(done){ + if (method === 'query' && shouldSkipQuery(process.versions.node)) { + this.skip() + } var app = express(); app[method]('/foo', function(req, res){ diff --git a/test/res.send.js b/test/res.send.js index 1e1835f8238..b4cf68a7df6 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -409,9 +409,11 @@ describe('res', function(){ methods.forEach(function (method) { if (method === 'connect') return; - if (method === 'query' && shouldSkipQuery(process.versions.node)) return it('should send ETag in response to ' + method.toUpperCase() + ' request', function (done) { + if (method === 'query' && shouldSkipQuery(process.versions.node)) { + this.skip() + } var app = express(); app[method]('/', function (req, res) { diff --git a/test/support/utils.js b/test/support/utils.js index a4d9fb8b548..5ad4ca98410 100644 --- a/test/support/utils.js +++ b/test/support/utils.js @@ -77,13 +77,10 @@ function getMajorVersion(versionString) { } function shouldSkipQuery(versionString) { - // Temporarily skipping this test on 22 - // update this implementation to run on those release lines on supported versions once they exist - // upstream tracking https://github.com/nodejs/node/pull/51719 + // Skipping HTTP QUERY tests on Node 21, it is reported in http.METHODS on 21.7.2 but not supported + // update this implementation to run on supported versions of 21 once they exist + // upstream tracking https://github.com/nodejs/node/issues/51562 // express tracking issue: https://github.com/expressjs/express/issues/5615 - var majorsToSkip = { - "22": true - } - return majorsToSkip[getMajorVersion(versionString)] + return Number(getMajorVersion(versionString)) === 21 } From 6d084715ba6ca5301e9ac1efe4309e555973b364 Mon Sep 17 00:00:00 2001 From: Chris de Almeida Date: Mon, 10 Jun 2024 16:19:11 -0500 Subject: [PATCH 464/479] =?UTF-8?q?=F0=9F=93=9D=20update=20people,=20add?= =?UTF-8?q?=20ctcpip=20to=20TC=20(#5683)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 01acf9e2f19..365c7b6104f 100644 --- a/Readme.md +++ b/Readme.md @@ -161,8 +161,6 @@ $ npm test The original author of Express is [TJ Holowaychuk](https://github.com/tj) -The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson) - [List of all contributors](https://github.com/expressjs/express/graphs/contributors) ### TC (Technical Committee) @@ -174,6 +172,7 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d * [blakeembrey](https://github.com/blakeembrey) - **Blake Embrey** * [sheplu](https://github.com/sheplu) - **Jean Burellier** * [crandmck](https://github.com/crandmck) - **Rand McKinney** +* [ctcpip](https://github.com/ctcpip) - **Chris de Almeida**
      TC emeriti members From 4cf7eed927d3ccd3f1d0c9a14d562ec0a1635e86 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Wed, 26 Jun 2024 18:23:19 -0400 Subject: [PATCH 465/479] remove minor version pinning from ci (#5722) --- .github/workflows/ci.yml | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 920db416d61..09004fec751 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,83 +61,83 @@ jobs: npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: io.js 1.x - node-version: "1.8" + node-version: "1" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: io.js 2.x - node-version: "2.5" + node-version: "2" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: io.js 3.x - node-version: "3.3" + node-version: "3" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: Node.js 4.x - node-version: "4.9" + node-version: "4" npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - name: Node.js 5.x - node-version: "5.12" + node-version: "5" npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - name: Node.js 6.x - node-version: "6.17" + node-version: "6" npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2 - name: Node.js 7.x - node-version: "7.10" + node-version: "7" npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - name: Node.js 8.x - node-version: "8.17" + node-version: "8" npm-i: mocha@7.2.0 nyc@14.1.1 - name: Node.js 9.x - node-version: "9.11" + node-version: "9" npm-i: mocha@7.2.0 nyc@14.1.1 - name: Node.js 10.x - node-version: "10.24" + node-version: "10" npm-i: mocha@8.4.0 - name: Node.js 11.x - node-version: "11.15" + node-version: "11" npm-i: mocha@8.4.0 - name: Node.js 12.x - node-version: "12.22" + node-version: "12" npm-i: mocha@9.2.2 - name: Node.js 13.x - node-version: "13.14" + node-version: "13" npm-i: mocha@9.2.2 - name: Node.js 14.x - node-version: "14.20" + node-version: "14" - name: Node.js 15.x - node-version: "15.14" + node-version: "15" - name: Node.js 16.x - node-version: "16.20" + node-version: "16" - name: Node.js 17.x - node-version: "17.9" + node-version: "17" - name: Node.js 18.x - node-version: "18.19" + node-version: "18" - name: Node.js 19.x - node-version: "19.9" + node-version: "19" - name: Node.js 20.x - node-version: "20.11" + node-version: "20" - name: Node.js 21.x - node-version: "21.7" + node-version: "21" - name: Node.js 22.x - node-version: "22.2" + node-version: "22" steps: - uses: actions/checkout@v4 From 2ec589c1133e2eec29a951b4976c50db638f7dd5 Mon Sep 17 00:00:00 2001 From: S M Mahmudul Hasan Date: Thu, 18 Jul 2024 02:44:03 +0600 Subject: [PATCH 466/479] Fix Contributor Covenant link definition reference in attribution section (#5762) --- Code-Of-Conduct.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code-Of-Conduct.md b/Code-Of-Conduct.md index bbb8996a659..ca4c6b31468 100644 --- a/Code-Of-Conduct.md +++ b/Code-Of-Conduct.md @@ -127,7 +127,7 @@ project community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant, version 2.0](cc-20-doc). +This Code of Conduct is adapted from the [Contributor Covenant, version 2.0][cc-20-doc]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). From f4bd86ed361ea9710ed0f7b4634e66c8e3b88b40 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Sat, 27 Jul 2024 14:15:55 -0400 Subject: [PATCH 467/479] Replace Appveyor windows testing with GHA (#5599) This PR moves us off of Appveyor for windows testing. We are now doing windows/linux testing on GHA. With the exception of iojs, which we are only testing on Linux and have split out to it's own workflow. We have also added npm-shrinkwrap.json to our gitignore, in order to not have to configure npm in CI to ignore it. If it's never checked in, it shouldn't exist in CI as you need to go out of your way to create it w/ npm. --- .github/workflows/ci.yml | 288 ++++++++++++++++--------------------- .github/workflows/iojs.yml | 69 +++++++++ .gitignore | 1 + package.json | 4 +- 4 files changed, 197 insertions(+), 165 deletions(-) create mode 100644 .github/workflows/iojs.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09004fec751..b3ddd6b23ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,187 +20,149 @@ concurrency: cancel-in-progress: true jobs: - test: + lint: + name: Lint runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js {{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + persist-credentials: false + + - name: Install dependencies + run: npm install --ignore-scripts --only=dev + + - name: Run lint + run: npm run lint + + test: + name: Run tests strategy: fail-fast: false 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 21.x - - Node.js 22.x - + os: [ubuntu-latest, windows-latest] + node-version: + - "0.10" + - "0.12" + - "4" + - "5" + - "6" + - "7" + - "8" + - "9" + - "10" + - "11" + - "12" + - "13" + - "14" + - "15" + - "16" + - "17" + - "18" + - "19" + - "20" + - "21" + - "22" + # Use supported versions of our testing tools under older versions of Node + # Install npm in some specific cases where we need to 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" - npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: io.js 2.x - node-version: "2" - npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: io.js 3.x - node-version: "3" - npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: Node.js 4.x - node-version: "4" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 5.x - node-version: "5" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 6.x - node-version: "6" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2 - - - name: Node.js 7.x - node-version: "7" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - - - name: Node.js 8.x - node-version: "8" - npm-i: mocha@7.2.0 nyc@14.1.1 + - node-version: "0.10" + npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0" + # Npm isn't being installed on windows w/ setup-node for + # 0.10 and 0.12, which will end up choking when npm uses es6 + npm-version: "npm@2.15.1" - - name: Node.js 9.x - node-version: "9" - npm-i: mocha@7.2.0 nyc@14.1.1 + - node-version: "0.12" + npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0" + npm-version: "npm@2.15.11" - - name: Node.js 10.x - node-version: "10" - npm-i: mocha@8.4.0 + - node-version: "4" + npm-i: "mocha@5.2.0 nyc@11.9.0 supertest@3.4.2" - - name: Node.js 11.x - node-version: "11" - npm-i: mocha@8.4.0 + - node-version: "5" + npm-i: "mocha@5.2.0 nyc@11.9.0 supertest@3.4.2" + # fixes https://github.com/npm/cli/issues/681 + npm-version: "npm@3.10.10" - - name: Node.js 12.x - node-version: "12" - npm-i: mocha@9.2.2 + - node-version: "6" + npm-i: "mocha@6.2.2 nyc@14.1.1 supertest@3.4.2" - - name: Node.js 13.x - node-version: "13" - npm-i: mocha@9.2.2 + - node-version: "7" + npm-i: "mocha@6.2.2 nyc@14.1.1 supertest@6.1.6" - - name: Node.js 14.x - node-version: "14" + - node-version: "8" + npm-i: "mocha@7.2.0 nyc@14.1.1" - - name: Node.js 15.x - node-version: "15" + - node-version: "9" + npm-i: "mocha@7.2.0 nyc@14.1.1" - - name: Node.js 16.x - node-version: "16" + - node-version: "10" + npm-i: "mocha@8.4.0" - - name: Node.js 17.x - node-version: "17" + - node-version: "11" + npm-i: "mocha@8.4.0" - - name: Node.js 18.x - node-version: "18" + - node-version: "12" + npm-i: "mocha@9.2.2" - - name: Node.js 19.x - node-version: "19" - - - name: Node.js 20.x - node-version: "20" - - - name: Node.js 21.x - node-version: "21" - - - name: Node.js 22.x - node-version: "22" + - node-version: "13" + npm-i: "mocha@9.2.2" + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - - name: Install Node.js ${{ matrix.node-version }} - shell: bash -eo pipefail -l {0} - run: | - nvm install --default ${{ matrix.node-version }} - dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" - - - name: Install npm module(s) ${{ matrix.npm-i }} - run: npm install --save-dev ${{ matrix.npm-i }} - if: matrix.npm-i != '' - - - name: Remove non-test dependencies - run: npm rm --silent --save-dev connect-redis - - - 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 - - - name: List environment - id: list_env - shell: bash - run: | - echo "node@$(node -v)" - echo "npm@$(npm -v)" - npm -s ls ||: - (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT" - - - name: Run tests - shell: bash - run: | - 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 - run: | - mv ./coverage "./${{ matrix.name }}" - mkdir ./coverage - mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}" - - - name: Upload code coverage - uses: actions/upload-artifact@v3 - with: - name: coverage - path: ./coverage - retention-days: 1 + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Npm version fixes + if: ${{matrix.npm-version != ''}} + run: npm install -g ${{ matrix.npm-version }} + + - name: Configure npm loglevel + run: | + npm config set loglevel error + shell: bash + + - name: Install dependencies + run: npm install + + - name: Install Node version specific dev deps + if: ${{ matrix.npm-i != '' }} + run: npm install --save-dev ${{ matrix.npm-i }} + + - name: Remove non-test dependencies + run: npm rm --silent --save-dev connect-redis + + - name: Output Node and NPM versions + run: | + echo "Node.js version: $(node -v)" + echo "NPM version: $(npm -v)" + + - name: Run tests + shell: bash + run: | + npm run test-ci + cp coverage/lcov.info "coverage/${{ matrix.node-version }}.lcov" + + - name: Collect code coverage + run: | + mv ./coverage "./${{ matrix.node-version }}" + mkdir ./coverage + mv "./${{ matrix.node-version }}" "./coverage/${{ matrix.node-version }}" + + - name: Upload code coverage + uses: actions/upload-artifact@v3 + with: + name: coverage + path: ./coverage + retention-days: 1 coverage: needs: test diff --git a/.github/workflows/iojs.yml b/.github/workflows/iojs.yml new file mode 100644 index 00000000000..c1268abd689 --- /dev/null +++ b/.github/workflows/iojs.yml @@ -0,0 +1,69 @@ +name: iojs-ci + +on: + push: + branches: + - master + - '4.x' + paths-ignore: + - '*.md' + pull_request: + paths-ignore: + - '*.md' + +concurrency: + group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: ["1.8", "2.5", "3.3"] + include: + - node-version: "1.8" + npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0" + - node-version: "2.5" + npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0" + - node-version: "3.3" + npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0" + + steps: + - uses: actions/checkout@v4 + + - name: Install iojs ${{ matrix.node-version }} + shell: bash -eo pipefail -l {0} + run: | + nvm install --default ${{ matrix.node-version }} + dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" + + - name: Configure npm + run: | + npm config set loglevel error + npm config set shrinkwrap false + + - name: Install npm module(s) ${{ matrix.npm-i }} + run: npm install --save-dev ${{ matrix.npm-i }} + if: matrix.npm-i != '' + + - name: Remove non-test dependencies + run: npm rm --silent --save-dev connect-redis + + - name: Install Node.js dependencies + run: npm install + + - name: List environment + id: list_env + shell: bash + run: | + echo "node@$(node -v)" + echo "npm@$(npm -v)" + npm -s ls ||: + (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT" + + - name: Run tests + shell: bash + run: npm run test + diff --git a/.gitignore b/.gitignore index 3a673d9cc09..1bd5c02b28b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # npm node_modules package-lock.json +npm-shrinkwrap.json *.log *.gz diff --git a/package.json b/package.json index 88e4206fe67..71781e11d64 100644 --- a/package.json +++ b/package.json @@ -91,8 +91,8 @@ "scripts": { "lint": "eslint .", "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/", - "test-ci": "nyc --reporter=lcovonly --reporter=text npm test", - "test-cov": "nyc --reporter=html --reporter=text npm test", + "test-ci": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=lcovonly --reporter=text npm test", + "test-cov": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=html --reporter=text npm test", "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/" } } From 2177f67f5439494f7a29a8d04f744cc20fb9f201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Sun, 28 Jul 2024 12:55:10 +0200 Subject: [PATCH 468/479] docs: add OSSF Scorecard badge (#5436) PR-URL: https://github.com/expressjs/express/pull/5436 --- Readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Readme.md b/Readme.md index 365c7b6104f..0fa719e237b 100644 --- a/Readme.md +++ b/Readme.md @@ -22,6 +22,8 @@ [![NPM Version][npm-version-image]][npm-url] [![NPM Install Size][npm-install-size-image]][npm-install-size-url] [![NPM Downloads][npm-downloads-image]][npm-downloads-url] +[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer] + ```js const express = require('express') @@ -252,4 +254,6 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj) [npm-install-size-url]: https://packagephobia.com/result?p=express [npm-url]: https://npmjs.org/package/express [npm-version-image]: https://badgen.net/npm/v/express +[ossf-scorecard-badge]: https://api.securityscorecards.dev/projects/github.com/expressjs/express/badge +[ossf-scorecard-visualizer]: https://kooltheba.github.io/openssf-scorecard-api-visualizer/#/projects/github.com/expressjs/express [Code of Conduct]: https://github.com/expressjs/express/blob/master/Code-Of-Conduct.md From f5b6e67aed1d8e81c30bd5be7bb88dbbfabfeb64 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 18 Aug 2024 13:37:51 -0500 Subject: [PATCH 469/479] docs: update scorecard link (#5814) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ulises Gascón --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 0fa719e237b..34362d856f7 100644 --- a/Readme.md +++ b/Readme.md @@ -254,6 +254,6 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj) [npm-install-size-url]: https://packagephobia.com/result?p=express [npm-url]: https://npmjs.org/package/express [npm-version-image]: https://badgen.net/npm/v/express -[ossf-scorecard-badge]: https://api.securityscorecards.dev/projects/github.com/expressjs/express/badge -[ossf-scorecard-visualizer]: https://kooltheba.github.io/openssf-scorecard-api-visualizer/#/projects/github.com/expressjs/express +[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge +[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/express [Code of Conduct]: https://github.com/expressjs/express/blob/master/Code-Of-Conduct.md From e35380a39d94937e3d0f7119e0efbc7cd69d003f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Mon, 19 Aug 2024 22:12:24 +0200 Subject: [PATCH 470/479] docs: add @IamLizu to the triage team (#5836) PR-URL: https://github.com/expressjs/express/pull/5836 --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 34362d856f7..bc108d55fc0 100644 --- a/Readme.md +++ b/Readme.md @@ -202,6 +202,7 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj) * [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi** * [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him) * [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego** +* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him) * [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
      From c5addb9a17c5b4c9fccdd2c04153a30595e03385 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Wed, 21 Aug 2024 20:15:02 -0700 Subject: [PATCH 471/479] deps: path-to-regexp@0.1.8 (#5603) --- History.md | 2 ++ package.json | 2 +- test/app.router.js | 26 ++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c02b24ffba2..d81f423d16f 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,8 @@ unreleased ========== + * deps: path-to-regexp@0.1.8 + - Adds support for named matching groups in the routes using a regex * deps: encodeurl@~2.0.0 - Removes encoding of `\`, `|`, and `^` to align better with URL spec * Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie` diff --git a/package.json b/package.json index 71781e11d64..e88618f997b 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.8", "proxy-addr": "~2.0.7", "qs": "6.11.0", "range-parser": "~1.2.1", diff --git a/test/app.router.js b/test/app.router.js index 707333f0432..8e427bd6dc7 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -193,6 +193,23 @@ describe('app.router', function(){ .expect('editing user 10', done); }) + if (supportsRegexp('(?.*)')) { + it('should populate req.params with named captures', function(done){ + var app = express(); + var re = new RegExp('^/user/(?[0-9]+)/(view|edit)?$'); + + app.get(re, function(req, res){ + var id = req.params.userId + , op = req.params[0]; + res.end(op + 'ing user ' + id); + }); + + request(app) + .get('/user/10/edit') + .expect('editing user 10', done); + }) + } + it('should ensure regexp matches path prefix', function (done) { var app = express() var p = [] @@ -1114,3 +1131,12 @@ describe('app.router', function(){ assert.strictEqual(app.get('/', function () {}), app) }) }) + +function supportsRegexp(source) { + try { + new RegExp(source) + return true + } catch (e) { + return false + } +} From a3e7e05e0a435b7b4be25bd38d8d0ca19a773ca9 Mon Sep 17 00:00:00 2001 From: S M Mahmudul Hasan Date: Thu, 22 Aug 2024 22:25:14 +0600 Subject: [PATCH 472/479] docs: specify new instructions for `question` and `discuss` PR-URL: https://github.com/expressjs/express/pull/5835 --- Triager-Guide.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Triager-Guide.md b/Triager-Guide.md index a2909ef30db..c15e6be5313 100644 --- a/Triager-Guide.md +++ b/Triager-Guide.md @@ -9,11 +9,18 @@ classification: * `needs triage`: This can be kept if the triager is unsure which next steps to take * `awaiting more info`: If more info has been requested from the author, apply this label. -* `question`: User questions that do not appear to be bugs or enhancements. -* `discuss`: Topics for discussion. Might end in an `enhancement` or `question` label. * `bug`: Issues that present a reasonable conviction there is a reproducible bug. * `enhancement`: Issues that are found to be a reasonable candidate feature additions. +If the issue is a question or discussion, it should be moved to GitHub Discussions. + +### Moving Discussions and Questions to GitHub Discussions + +For issues labeled with `question` or `discuss`, it is recommended to move them to GitHub Discussions instead: + +* **Questions**: User questions that do not appear to be bugs or enhancements should be moved to GitHub Discussions. +* **Discussions**: Topics for discussion should be moved to GitHub Discussions. If the discussion leads to a new feature or bug identification, it can be moved back to Issues. + In all cases, issues may be closed by maintainers if they don't receive a timely response when further information is sought, or when additional questions are asked. From 2a980ad16052e53b398c9953fea50e3daa0b495c Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Fri, 23 Aug 2024 22:39:13 +0200 Subject: [PATCH 473/479] merge-descriptors@1.0.3 (#5781) * Allow patches for `merge-descriptors` dependency * Set fixed latest of v1 (1.0.3) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e88618f997b..91e29013a33 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "finalhandler": "1.2.0", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", From 125bb742a38cd97938a3932b47cc301e41c31f5d Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Mon, 9 Sep 2024 14:02:06 -0700 Subject: [PATCH 474/479] path-to-regexp@0.1.10 (#5902) * path-to-regexp@0.1.10 * Update History.md --- History.md | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index d81f423d16f..b0aa2e0959e 100644 --- a/History.md +++ b/History.md @@ -1,8 +1,9 @@ unreleased ========== - * deps: path-to-regexp@0.1.8 + * deps: path-to-regexp@0.1.10 - Adds support for named matching groups in the routes using a regex + - Adds backtracking protection to parameters without regexes defined * deps: encodeurl@~2.0.0 - Removes encoding of `\`, `|`, and `^` to align better with URL spec * Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie` diff --git a/package.json b/package.json index 91e29013a33..87cf10be9c2 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.8", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", "qs": "6.11.0", "range-parser": "~1.2.1", From 54271f69b511fea198471e6ff3400ab805d6b553 Mon Sep 17 00:00:00 2001 From: Chris de Almeida Date: Mon, 9 Sep 2024 17:16:58 -0500 Subject: [PATCH 475/479] fix: don't render redirect values in anchor href MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ulises Gascón --- lib/response.js | 2 +- test/res.redirect.js | 24 +++++++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/response.js b/lib/response.js index 68d969ff05b..76b6b54a3b8 100644 --- a/lib/response.js +++ b/lib/response.js @@ -969,7 +969,7 @@ res.redirect = function redirect(url) { html: function(){ var u = escapeHtml(address); - body = '

      ' + statuses.message[status] + '. Redirecting to ' + u + '

      ' + body = '

      ' + statuses.message[status] + '. Redirecting to ' + u + '

      ' }, default: function(){ diff --git a/test/res.redirect.js b/test/res.redirect.js index 5ffc7e48f12..f7214d93312 100644 --- a/test/res.redirect.js +++ b/test/res.redirect.js @@ -106,7 +106,7 @@ describe('res', function(){ .set('Accept', 'text/html') .expect('Content-Type', /html/) .expect('Location', 'http://google.com') - .expect(302, '

      Found. Redirecting to http://google.com

      ', done) + .expect(302, '

      Found. Redirecting to http://google.com

      ', done) }) it('should escape the url', function(done){ @@ -122,9 +122,27 @@ describe('res', function(){ .set('Accept', 'text/html') .expect('Content-Type', /html/) .expect('Location', '%3Cla\'me%3E') - .expect(302, '

      Found. Redirecting to %3Cla'me%3E

      ', done) + .expect(302, '

      Found. Redirecting to %3Cla'me%3E

      ', done) }) + it('should not render evil javascript links in anchor href (prevent XSS)', function(done){ + var app = express(); + var xss = 'javascript:eval(document.body.innerHTML=`

      XSS

      `);'; + var encodedXss = 'javascript:eval(document.body.innerHTML=%60%3Cp%3EXSS%3C/p%3E%60);'; + + app.use(function(req, res){ + res.redirect(xss); + }); + + request(app) + .get('/') + .set('Host', 'http://example.com') + .set('Accept', 'text/html') + .expect('Content-Type', /html/) + .expect('Location', encodedXss) + .expect(302, '

      Found. Redirecting to ' + encodedXss +'

      ', done); + }); + it('should include the redirect type', function(done){ var app = express(); @@ -137,7 +155,7 @@ describe('res', function(){ .set('Accept', 'text/html') .expect('Content-Type', /html/) .expect('Location', 'http://google.com') - .expect(301, '

      Moved Permanently. Redirecting to http://google.com

      ', done); + .expect(301, '

      Moved Permanently. Redirecting to http://google.com

      ', done); }) }) From ec4a01b6b8814d7b007f36a3023f4dbafdbc3d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Tue, 10 Sep 2024 01:36:30 +0200 Subject: [PATCH 476/479] feat: upgrade to body-parser@1.20.3 (#5926) PR-URL: https://github.com/expressjs/express/pull/5926 --- History.md | 5 ++++- package.json | 2 +- test/express.urlencoded.js | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index b0aa2e0959e..904db45b233 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,9 @@ unreleased ========== - + * deps: body-parser@0.6.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`) + * Remove link renderization in html while using `res.redirect` * deps: path-to-regexp@0.1.10 - Adds support for named matching groups in the routes using a regex - Adds backtracking protection to parameters without regexes defined diff --git a/package.json b/package.json index 87cf10be9c2..4c0fea2d2e5 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", diff --git a/test/express.urlencoded.js b/test/express.urlencoded.js index e07432c86c3..537fb797e75 100644 --- a/test/express.urlencoded.js +++ b/test/express.urlencoded.js @@ -212,7 +212,7 @@ describe('express.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]' } @@ -230,7 +230,7 @@ describe('express.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) }) From 9ebe5d500d22cbb2b8aaa73446866b084c747971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Tue, 10 Sep 2024 02:46:25 +0200 Subject: [PATCH 477/479] feat: upgrade to send@0.19.0 (#5928) --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 904db45b233..9f47885e3d2 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,7 @@ unreleased ========== + * deps: send@0.19.0 + * Remove link renderization in html while redirecting * deps: body-parser@0.6.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`) diff --git a/package.json b/package.json index 4c0fea2d2e5..1dc8c5b70cb 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", + "send": "0.19.0", "serve-static": "1.15.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", From 4c9ddc1c47bf579e55c2fe837d76a952e9fd8959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Tue, 10 Sep 2024 03:24:32 +0200 Subject: [PATCH 478/479] feat: upgrade to serve-static@0.16.0 --- History.md | 2 ++ package.json | 2 +- test/express.static.js | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 9f47885e3d2..3fe5fc7aaf7 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,7 @@ unreleased ========== + * deps: serve-static@0.16.0 + * Remove link renderization in html while redirecting * deps: send@0.19.0 * Remove link renderization in html while redirecting * deps: body-parser@0.6.0 diff --git a/package.json b/package.json index 1dc8c5b70cb..e9045763b01 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", - "serve-static": "1.15.0", + "serve-static": "1.16.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", diff --git a/test/express.static.js b/test/express.static.js index 245fd5929cc..23e607ed933 100644 --- a/test/express.static.js +++ b/test/express.static.js @@ -486,7 +486,7 @@ describe('express.static()', function () { request(this.app) .get('/users') .expect('Location', '/users/') - .expect(301, //, done) + .expect(301, /\/users\//, done) }) it('should redirect directories with query string', function (done) { @@ -508,7 +508,7 @@ describe('express.static()', function () { .get('/snow') .expect('Location', '/snow%20%E2%98%83/') .expect('Content-Type', /html/) - .expect(301, />Redirecting to \/snow%20%E2%98%83\/<\/a>Redirecting to \/snow%20%E2%98%83\/ Date: Tue, 10 Sep 2024 03:32:10 +0200 Subject: [PATCH 479/479] 4.20.0 --- History.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 3fe5fc7aaf7..887a38f182d 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,4 @@ -unreleased +4.20.0 / 2024-09-10 ========== * deps: serve-static@0.16.0 * Remove link renderization in html while redirecting diff --git a/package.json b/package.json index e9045763b01..bffa70a6f1c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.19.2", + "version": "4.20.0", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ",