diff --git a/lib/ecstatic/status-handlers.js b/lib/ecstatic/status-handlers.js index f64c4b5..9874534 100644 --- a/lib/ecstatic/status-handlers.js +++ b/lib/ecstatic/status-handlers.js @@ -60,11 +60,13 @@ exports['416'] = function (res, next) { // flagrant error exports['500'] = function (res, next, opts) { res.statusCode = 500; + res.setHeader('content-type', 'text/plain'); res.end(opts.error.stack || opts.error.toString() || "No specified error"); }; // bad request exports['400'] = function (res, next, opts) { res.statusCode = 400; + res.setHeader('content-type', 'text/plain'); res.end(opts && opts.error ? String(opts.error) : 'Malformed request.'); }; diff --git a/test/html-reflection.js b/test/html-reflection.js new file mode 100644 index 0000000..4543cdc --- /dev/null +++ b/test/html-reflection.js @@ -0,0 +1,32 @@ +var test = require('tap').test, + ecstatic = require('../'), + http = require('http'), + request = require('request'); + +var server; + +test('html reflection prevented', function (t) { + server = http.createServer(ecstatic(__dirname + '/public/containsSymlink')); + + server.listen(0, function () { + var port = server.address().port; + var attack = ''; + request.get('http://localhost:' + port + '/more-problematic/' + attack, function (err, res, body) { + if ((!res.headers['content-type'] || res.headers['content-type'] == 'text/html') && + body.indexOf(attack) != -1) { + t.fail('Unescaped HTML reflected with vulnerable or missing content-type.'); + } + t.end(); + }); + }); +}); + +test('server teardown', function (t) { + server.close(); + + var to = setTimeout(function () { + process.stderr.write('# server not closing; slaughtering process.\n'); + process.exit(0); + }, 5000); + t.end(); +}); diff --git a/test/public/containsSymlink/more-problematic b/test/public/containsSymlink/more-problematic new file mode 120000 index 0000000..433c936 --- /dev/null +++ b/test/public/containsSymlink/more-problematic @@ -0,0 +1 @@ +/root \ No newline at end of file