Currently, send resolves file paths using string normalization but does not check whether the final canonical path (after symlink resolution) remains inside the configured root. This means symbolic links inside the root can point to — and serve — files outside of it.
Proposal
Add a followSymlinks option (default: true for backward compatibility):
true — current behavior, symlinks are followed.
false — resolve the canonical path via fs.realpath() and reject the
request if it falls outside root.
Example implementation sketch:
if (!options.followSymlinks) {
const real = await fs.promises.realpath(path)
if (!real.startsWith(root + path.sep) && real !== root) {
return this.error(403)
}
}
Motivation
This gives developers an explicit opt-in safeguard, similar to how dotfiles already provides control over another filesystem-level concern. It is especially useful for applications that serve user-writable directories.
Backward Compatibility
Defaults to true, so no breaking change just a semver-minor.
Notes
Original idea by @san3ncrypt3d
Currently,
sendresolves file paths using string normalization but does not check whether the final canonical path (after symlink resolution) remains inside the configuredroot. This means symbolic links inside the root can point to — and serve — files outside of it.Proposal
Add a
followSymlinksoption (default:truefor backward compatibility):true— current behavior, symlinks are followed.false— resolve the canonical path viafs.realpath()and reject therequest if it falls outside
root.Example implementation sketch:
Motivation
This gives developers an explicit opt-in safeguard, similar to how
dotfilesalready provides control over another filesystem-level concern. It is especially useful for applications that serve user-writable directories.Backward Compatibility
Defaults to
true, so no breaking change just a semver-minor.Notes
Original idea by @san3ncrypt3d