Skip to content

Commit

Permalink
Add "root" option to res.download
Browse files Browse the repository at this point in the history
fixes #4834
closes #4855
  • Loading branch information
mmito authored and dougwilson committed Mar 25, 2022
1 parent 4847d0e commit 0def9bb
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 1 deletion.
1 change: 1 addition & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -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`
Expand Down
4 changes: 3 additions & 1 deletion lib/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
74 changes: 74 additions & 0 deletions test/res.download.js
Original file line number Diff line number Diff line change
Expand Up @@ -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){
Expand Down Expand Up @@ -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(){
Expand Down

0 comments on commit 0def9bb

Please sign in to comment.