diff --git a/History.md b/History.md index 9a99efda..bc318420 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ - previous versions are not vulnerable; this is just explicit protection * deprecate `res.redirect(url, status)` -- use `res.redirect(status, url)` instead * fix `res.send(status, num)` to send `num` as json (not error) + * remove unnecessary escaping when `res.jsonp` returns JSON response * deps: basic-auth@1.0.0 - support empty password - support empty username diff --git a/lib/response.js b/lib/response.js index 345597b8..2e4b35cb 100644 --- a/lib/response.js +++ b/lib/response.js @@ -260,9 +260,7 @@ res.jsonp = function(obj){ var app = this.app; var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); - var body = JSON.stringify(obj, replacer, spaces) - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029'); + var body = JSON.stringify(obj, replacer, spaces); var callback = this.req.query[app.get('jsonp callback name')]; // content-type @@ -282,10 +280,18 @@ res.jsonp = function(obj){ this.charset = 'utf-8'; this.set('X-Content-Type-Options', 'nosniff'); this.set('Content-Type', 'text/javascript'); - var cb = callback.replace(/[^\[\]\w$.]/g, ''); + + // restrict callback charset + callback = callback.replace(/[^\[\]\w$.]/g, ''); + + // replace chars not allowed in JavaScript that are in JSON + body = body + .replace(/\u2028/g, '\\u2028') + .replace(/\u2029/g, '\\u2029'); // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse" - body = '/**/ typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');'; + // the typeof check is just to reduce client error noise + body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');'; } return this.send(body); diff --git a/test/res.jsonp.js b/test/res.jsonp.js index 45acb906..2c5e245f 100644 --- a/test/res.jsonp.js +++ b/test/res.jsonp.js @@ -98,6 +98,19 @@ describe('res', function(){ .expect(200, /foo\(\{"str":"\\u2028 \\u2029 woot"\}\);/, done); }); + it('should not escape utf whitespace for json fallback', function(done){ + var app = express(); + + app.use(function(req, res){ + res.jsonp({ str: '\u2028 \u2029 woot' }); + }); + + request(app) + .get('/') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200, '{"str":"\u2028 \u2029 woot"}', done); + }); + it('should include security header and prologue', function (done) { var app = express();