Permalink
Browse files

Support symlinks that point outside the root path.

Also added lots of new path traversal tests for peace of mind.
  • Loading branch information...
1 parent b1c3700 commit 7e7ebbaa7f3600d2c69d66b38e0687b9d27aede2 @rgrove committed Jan 20, 2012
View
@@ -1,6 +1,12 @@
Combo Handler History
=====================
+0.1.4 (git)
+-----------
+
+* Added support for symlinks that point to files outside the root path.
+
+
0.1.3 (2011-10-31)
------------------
View
@@ -55,34 +55,34 @@ exports.combine = function (config) {
return;
}
- fs.realpath(path.normalize(path.join(rootPath, relativePath)), function (err, absolutePath) {
- // Bubble up an error if the file can't be found or if the request
- // attempts to traverse above the root path.
- if (err || !absolutePath || absolutePath.indexOf(rootPath) !== 0) {
- return next(new BadRequest);
- }
+ var absolutePath = path.normalize(path.join(rootPath, relativePath));
- fs.stat(absolutePath, function (err, stats) {
- if (err || !stats.isFile()) { return next(new BadRequest); }
+ // Bubble up an error if the request attempts to traverse above the
+ // root path.
+ if (!absolutePath || absolutePath.indexOf(rootPath) !== 0) {
+ return next(new BadRequest);
+ }
- var mtime = new Date(stats.mtime);
+ fs.stat(absolutePath, function (err, stats) {
+ if (err || !stats.isFile()) { return next(new BadRequest); }
- if (!lastModified || mtime > lastModified) {
- lastModified = mtime;
- }
+ var mtime = new Date(stats.mtime);
- fs.readFile(absolutePath, 'utf8', function (err, data) {
- if (err) { return next(new BadRequest); }
+ if (!lastModified || mtime > lastModified) {
+ lastModified = mtime;
+ }
+
+ fs.readFile(absolutePath, 'utf8', function (err, data) {
+ if (err) { return next(new BadRequest); }
- body[i] = data;
- pending -= 1;
+ body[i] = data;
+ pending -= 1;
- if (pending === 0) {
- finish();
- }
- }); // fs.readFile
- }); // fs.stat
- }); // fs.realpath
+ if (pending === 0) {
+ finish();
+ }
+ }); // fs.readFile
+ }); // fs.stat
}); // forEach
};
};
View
@@ -0,0 +1 @@
+outside();
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View
@@ -15,8 +15,8 @@ describe('combohandler', function () {
before(function () {
app = server({
roots: {
- '/css': __dirname + '/fixtures/css',
- '/js' : __dirname + '/fixtures/js'
+ '/css': __dirname + '/fixtures/root/css',
+ '/js' : __dirname + '/fixtures/root/js'
}
});
@@ -49,33 +49,66 @@ describe('combohandler', function () {
});
});
- it('should return a 400 Bad Request error when no files are specified', function (done) {
- request(BASE_URL + '/js', function (err, res, body) {
+ it('should support symlinks pointing outside the root', function (done) {
+ request(BASE_URL + '/js?a.js&b.js&outside.js', function (err, res, body) {
assert.equal(err, null);
- res.should.have.status(400);
- body.should.equal('Bad request.');
+ res.should.have.status(200);
+ res.should.have.header('content-type', 'application/javascript;charset=utf-8');
+ res.should.have.header('last-modified');
+ body.should.equal('a();\n\nb();\n\noutside();\n');
done();
});
});
+ // -- Errors ---------------------------------------------------------------
+ describe('errors', function () {
+ it('should return a 400 Bad Request error when no files are specified', function (done) {
+ request(BASE_URL + '/js', function (err, res, body) {
+ assert.equal(err, null);
+ res.should.have.status(400);
+ body.should.equal('Bad request.');
+ done();
+ });
+ });
- it('should throw a 400 Bad Request error when a file is not found', function (done) {
- request(BASE_URL + '/js?bogus.js', function (err, res, body) {
- assert.equal(err, null);
- res.should.have.status(400);
- res.should.have.header('content-type', 'text/plain; charset=utf-8');
- body.should.equal('Bad request.');
- done();
+
+ it('should throw a 400 Bad Request error when a file is not found', function (done) {
+ request(BASE_URL + '/js?bogus.js', function (err, res, body) {
+ assert.equal(err, null);
+ res.should.have.status(400);
+ res.should.have.header('content-type', 'text/plain; charset=utf-8');
+ body.should.equal('Bad request.');
+ done();
+ });
});
- });
- it('should throw a 400 Bad Request error when directory traversal is attempted', function (done) {
- request(BASE_URL + '/js?../../../package.json', function (err, res, body) {
- assert.equal(err, null);
- res.should.have.status(400);
- res.should.have.header('content-type', 'text/plain; charset=utf-8');
- body.should.equal('Bad request.');
- done();
+ describe('path traversal', function () {
+ var paths = [
+ '../../../../package.json',
+ '..%2f..%2f..%2f..%2fpackage.json',
+ '%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fpackage.json',
+ '%2e%2e/%2e%2e/%2e%2e/%2e%2e/package.json',
+ '..\\..\\..\\..\\package.json',
+ '..%5c..%5c..%5c..%5cpackage.json',
+ '%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5cpackage.json',
+ '%2e%2e\\%2e%2e\\%2e%2e\\%2e%2e\\package.json',
+ '....//....//....//....//package.json',
+ '....%2f%2f....%2f%2f....%2f%2f....%2f%2fpackage.json',
+ '....\\\\....\\\\....\\\\....\\\\package.json',
+ '....%5c%5c....%5c%5c....%5c%5c....%5c%5cpackage.json'
+ ];
+
+ paths.forEach(function (path) {
+ it('should throw a 400 Bad Request error when path traversal is attempted: ' + path, function (done) {
+ request(BASE_URL + '/js?' + path, function (err, res, body) {
+ assert.equal(err, null);
+ res.should.have.status(400);
+ res.should.have.header('content-type', 'text/plain; charset=utf-8');
+ body.should.equal('Bad request.');
+ done();
+ });
+ });
+ });
});
});
});

0 comments on commit 7e7ebba

Please sign in to comment.