Skip to content

Commit

Permalink
Add "escape json" setting for res.json and res.jsonp
Browse files Browse the repository at this point in the history
closes #3268
closes #3269
  • Loading branch information
Greg Guthe authored and dougwilson committed Sep 28, 2017
1 parent 628438d commit 7154014
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 5 deletions.
1 change: 1 addition & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
36 changes: 31 additions & 5 deletions lib/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
22 changes: 22 additions & 0 deletions test/res.json.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({ '&': '<script>' })
})

request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"\\u0026":"\\u003cscript\\u003e"}', done)
})
})

describe('"json replacer" setting', function(){
it('should be passed to JSON.stringify()', function(done){
var app = express();
Expand Down
22 changes: 22 additions & 0 deletions test/res.jsonp.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,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.jsonp({ '&': '\u2028<script>\u2029' })
})

request(app)
.get('/?callback=foo')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /foo\({"\\u0026":"\\u2028\\u003cscript\\u003e\\u2029"}\)/, done)
})
})

describe('"json replacer" setting', function(){
it('should be passed to JSON.stringify()', function(done){
var app = express();
Expand Down

0 comments on commit 7154014

Please sign in to comment.