Skip to content

add followSymlinks option to restrict symlink traversal #297

Description

@UlisesGascon

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

Activity

  1. changed the title [-]followSymlinks option to restrict symlink traversal[/-] [+]add `followSymlinks` option to restrict symlink traversal[/+] on Feb 23, 2026
  2. added a commit that references this issue on Feb 24, 2026
    c6da2a4
  3. san3ncrypt3d commented on Feb 27, 2026

    @san3ncrypt3d
  4. blakeembrey commented on Mar 21, 2026

    @blakeembrey
    Member

    A heads up, but there is an ongoing plan to refactor some of this API to decouple it a bit from node HTTP request/response (#159). If that occurs, this might be a good candidate for a feature that lives upstream of the package, as we can return the filename/stream and it can be used to alter the behavior before sending it to res. This feature may also be able to tackle the request in #71 too.

    So I'd like to hold off on this in the immediate future and potentially re-investigate in a few months.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions