Skip to content

Commit

Permalink
Merge 269d1ae into 3e9bc12
Browse files Browse the repository at this point in the history
  • Loading branch information
haoxin committed Feb 4, 2015
2 parents 3e9bc12 + 269d1ae commit ae37a1e
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 43 deletions.
5 changes: 3 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ Note that the module will try to serve the gzipped version of a file automatical

## Root path

Note that when `root` is _not_ used you __MUST__ provide an _absolute_
path, and this path must not contain "..", protecting developers from
Note that `root` is required, defaults to `''` and will be resolved,
removing the leading `/` to make the path relative and this
path must not contain "..", protecting developers from
concatenating user input. If you plan on serving files based on
user input supply a `root` directory from which to serve from.

Expand Down
16 changes: 3 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
*/

var debug = require('debug')('koa-send');
var resolvePath = require('resolve-path');
var assert = require('assert');
var path = require('path');
var normalize = path.normalize;
var basename = path.basename;
var extname = path.extname;
var resolve = path.resolve;
var fs = require('mz/fs');
var join = path.join;

/**
* Expose `send()`.
Expand All @@ -37,6 +37,7 @@ function send(ctx, path, opts) {
// options
debug('send "%s" %j', path, opts);
var root = opts.root ? normalize(resolve(opts.root)) : '';
path = path[0] == '/' ? path.slice(1) : path;
var index = opts.index;
var maxage = opts.maxage || opts.maxAge || 0;
var hidden = opts.hidden || false;
Expand All @@ -50,21 +51,10 @@ function send(ctx, path, opts) {

if (-1 == path) return ctx.throw('failed to decode', 400);

// null byte(s)
if (~path.indexOf('\0')) return ctx.throw('null bytes', 400);

// index file support
if (index && trailingSlash) path += index;

// malicious path
if (!root && !isAbsolute(path)) return ctx.throw('relative paths require the .root option', 500);
if (!root && ~path.indexOf('..')) return ctx.throw('malicious path', 400);

// relative to root
path = normalize(join(root, path));

// out of bounds
if (root && 0 !== path.indexOf(root)) return ctx.throw('malicious path', 400);
path = resolvePath(root, path);

// hidden file support, ignore
if (!hidden && leadingDot(path)) return;
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"license": "MIT",
"dependencies": {
"debug": "*",
"mz": "^1.0.1"
"mz": "^1.0.1",
"resolve-path": "^1.2.1"
},
"scripts": {
"test": "mocha --harmony-generators --require should --reporter spec",
Expand Down
54 changes: 27 additions & 27 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var assert = require('assert');
describe('send(ctx, file)', function(){
describe('with no .root', function(){
describe('when the path is absolute', function(){
it('should serve the file', function(done){
it('should 404', function(done){
var app = koa();

app.use(function *(){
Expand All @@ -16,13 +16,12 @@ describe('send(ctx, file)', function(){

request(app.listen())
.get('/')
.expect(200)
.expect('world', done);
.expect(404, done);
})
})

describe('when the path is relative', function(){
it('should 500', function(done){
it('should 200', function(done){
var app = koa();

app.use(function *(){
Expand All @@ -31,21 +30,22 @@ describe('send(ctx, file)', function(){

request(app.listen())
.get('/')
.expect(500, done);
.expect(200)
.expect('world', done);
})
})

describe('when the path contains ..', function(){
it('should 400', function(done){
it('should 403', function(done){
var app = koa();

app.use(function *(){
yield send(this, __dirname + '/../fixtures/hello.txt');
yield send(this, '/../fixtures/hello.txt');
});

request(app.listen())
.get('/')
.expect(400, done);
.expect(403, done);
})
})
})
Expand Down Expand Up @@ -98,7 +98,7 @@ describe('send(ctx, file)', function(){
})

describe('when the path resolves above the root', function(){
it('should 400', function(done){
it('should 403', function(done){
var app = koa();

app.use(function *(){
Expand All @@ -108,12 +108,12 @@ describe('send(ctx, file)', function(){

request(app.listen())
.get('/')
.expect(400, done);
.expect(403, done);
})
})

describe('when the path resolves within root', function(){
it('should 400', function(done){
it('should 403', function(done){
var app = koa();

app.use(function *(){
Expand All @@ -123,8 +123,7 @@ describe('send(ctx, file)', function(){

request(app.listen())
.get('/')
.expect(200)
.expect('html index', done);
.expect(403, done);
})
})
})
Expand Down Expand Up @@ -152,7 +151,7 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
yield send(this, __dirname + '/test');
yield send(this, '/test');
});

request(app.listen())
Expand All @@ -164,7 +163,7 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
var sent = yield send(this, __dirname + '/test');
var sent = yield send(this, '/test');
assert.equal(sent, undefined);
});

Expand All @@ -179,7 +178,7 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
yield send(this, __dirname + '/fixtures');
yield send(this, '/test/fixtures');
});

request(app.listen())
Expand All @@ -194,9 +193,9 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
var p = __dirname + '/fixtures/user.json';
var p = '/test/fixtures/user.json';
var sent = yield send(this, p);
assert.equal(sent, p);
assert.equal(sent, __dirname + '/fixtures/user.json');
});

request(app.listen())
Expand All @@ -209,7 +208,7 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
yield send(this, __dirname + '/fixtures/gzip.json');
yield send(this, '/test/fixtures/gzip.json');
});

request(app.listen())
Expand All @@ -218,11 +217,12 @@ describe('send(ctx, file)', function(){
.expect('Content-Length', 18)
.expect(200, done);
})

it('should return .gz path', function(done){
var app = koa();

app.use(function *(){
yield send(this, __dirname + '/fixtures/gzip.json');
yield send(this, '/test/fixtures/gzip.json');
});

request(app.listen())
Expand All @@ -238,9 +238,9 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
var p = __dirname + '/fixtures/user.json';
var p = '/test/fixtures/user.json';
var sent = yield send(this, p, { maxage: 5000 });
assert.equal(sent, p);
assert.equal(sent, __dirname + '/fixtures/user.json');
});

request(app.listen())
Expand All @@ -253,9 +253,9 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
var p = __dirname + '/fixtures/user.json';
var p = '/test/fixtures/user.json';
var sent = yield send(this, p, { maxage: 1234 });
assert.equal(sent, p);
assert.equal(sent, __dirname + '/fixtures/user.json');
});

request(app.listen())
Expand All @@ -270,7 +270,7 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
yield send(this, __dirname + '/fixtures/user.json');
yield send(this, '/test/fixtures/user.json');
});

request(app.listen())
Expand All @@ -283,7 +283,7 @@ describe('send(ctx, file)', function(){
var app = koa();

app.use(function *(){
yield send(this, __dirname + '/fixtures/user.json');
yield send(this, '/test/fixtures/user.json');
});

request(app.listen())
Expand All @@ -297,7 +297,7 @@ describe('send(ctx, file)', function(){
var stream

app.use(function *(){
yield send(this, __dirname + '/fixtures/user.json');
yield send(this, '/test/fixtures/user.json');
stream = this.body;
this.socket.emit('error', new Error('boom'));
})
Expand Down

0 comments on commit ae37a1e

Please sign in to comment.