Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'followsymlinks' option which allows rejecting symlinks #127

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Prev Previous commit
Next Next commit
Fix tests on fallthrough behavior, linting issues and add docs for fo…
…llowymlinks
jayk committed Oct 10, 2019
commit 84dddc9202ccd41ee633ba756e566a1ee07d24dd
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -92,6 +92,15 @@ all methods.

The default value is `true`.

##### followsymlinks

Determines how serve-static will handle how files or paths containing symlinks
are handled. Setting `followsymlinks` to `false` will cause serve-static to
reject requests for files that have a symlink in their path.

The default value is `true`.


##### immutable

Enable or disable the `immutable` directive in the `Cache-Control` response
14 changes: 8 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
@@ -57,8 +57,8 @@ function serveStatic (root, options) {

// only set followsymlinks to false if it was explicitly set
if (opts.followsymlinks === false) {
followsymlinks = false
realroot = fs.realpathSync(root)
followsymlinks = false
realroot = fs.realpathSync(root)
}

// default redirect
@@ -104,18 +104,20 @@ function serveStatic (root, options) {
path = ''
}

if (followsymlinks == false) {
if (followsymlinks === false) {
fullpath = realroot + path
realpath = fs.realpathSync(realroot+path)
// if the full path and the real path are not the same,
realpath = fs.realpathSync(realroot + path)
// if the full path and the real path are not the same,
// then there is a symlink somewhere along the way
if (fullpath != realpath) {
if (fallthrough) {
return next()
}

// forbidden on symlinks
this.error(403)
res.statusCode = 403
res.setHeader('Content-Length', '0')
res.end()
return
}
}
29 changes: 24 additions & 5 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -764,7 +764,7 @@ describe('serveStatic()', function () {
describe('when followsymlinks is false', function () {
var server
before(function () {
server = createServer(fixtures, { followsymlinks: false });
server = createServer(fixtures, { followsymlinks: false, fallthrough: false })
})

it('accessing a real file works', function (done) {
@@ -776,21 +776,20 @@ describe('serveStatic()', function () {
it('should 403 on a symlink', function (done) {
request(server)
.get('/members/tobi.txt')
.expect(404, done)
.expect(403, done)
})

it('should fail on nested root symlink', function (done) {
request(server)
.get('/symroot/users/tobi.txt')
.expect(404, done)
.expect(403, done)
})

})

describe('when followsymlinks is false and root had symlinks', function () {
var server
before(function () {
server = createServer(fixtures + "/symroot", { followsymlinks: false });
server = createServer(fixtures + '/symroot', { followsymlinks: false, fallthrough: false })
})

it('accessing a real file works', function (done) {
@@ -800,11 +799,31 @@ describe('serveStatic()', function () {
})

it('should 403 on a symlink', function (done) {
request(server)
.get('/members/tobi.txt')
.expect(403, done)
})
})

describe('when followsymlinks is false and fallthrough is true', function () {
var server
before(function () {
server = createServer(fixtures, { followsymlinks: false, fallthrough: true })
})

it('accessing a real file works', function (done) {
request(server)
.get('/users/tobi.txt')
.expect(200, 'ferret', done)
})

it('should 404 on a symlink', function (done) {
request(server)
.get('/members/tobi.txt')
.expect(404, done)
})
})

describe('when responding non-2xx or 304', function () {
var server
before(function () {