diff --git a/History.md b/History.md index 1cf98094c5..fcb2fa5069 100644 --- a/History.md +++ b/History.md @@ -176,9 +176,19 @@ This is the first Express 5.0 alpha release, based off 4.10.1. * add: - `app.router` is a reference to the base router -unreleased +4.20.0 / 2024-09-10 ========== - + * 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 + * 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 * 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/Readme.md b/Readme.md index 0fa719e237..bc108d55fc 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**
@@ -254,6 +255,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 diff --git a/Triager-Guide.md b/Triager-Guide.md index a2909ef30d..c15e6be531 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. diff --git a/lib/response.js b/lib/response.js index a1bef622b9..b9b27097ae 100644 --- a/lib/response.js +++ b/lib/response.js @@ -837,7 +837,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/package.json b/package.json index fbda77075f..f6ec0b0f65 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,11 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", +<<<<<<< HEAD "version": "5.0.0-beta.3", +======= + "version": "4.20.0", +>>>>>>> master "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", @@ -42,7 +46,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", "mime-types": "~2.1.34", "on-finished": "2.4.1", diff --git a/test/app.router.js b/test/app.router.js index 7977016d99..73957eabfa 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -194,6 +194,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 = [] @@ -1140,3 +1157,12 @@ describe('app.router', function(){ assert.strictEqual(app.get('/', function () {}), app) }) }) + +function supportsRegexp(source) { + try { + new RegExp(source) + return true + } catch (e) { + return false + } +} diff --git a/test/express.static.js b/test/express.static.js index 57a23accc1..e5100e8c8d 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\/Found. Redirecting to http://google.com

', done) + .expect(302, '

Found. Redirecting to http://google.com

', done) }) it('should escape the url', function(done){ @@ -107,9 +107,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(); @@ -122,7 +140,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); }) })