...

Text file src/github.com/cyphar/filepath-securejoin/README.md

Documentation: github.com/cyphar/filepath-securejoin

     1## `filepath-securejoin` ##
     2
     3[![Build Status](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml/badge.svg)](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml)
     4
     5An implementation of `SecureJoin`, a [candidate for inclusion in the Go
     6standard library][go#20126]. The purpose of this function is to be a "secure"
     7alternative to `filepath.Join`, and in particular it provides certain
     8guarantees that are not provided by `filepath.Join`.
     9
    10> **NOTE**: This code is *only* safe if you are not at risk of other processes
    11> modifying path components after you've used `SecureJoin`. If it is possible
    12> for a malicious process to modify path components of the resolved path, then
    13> you will be vulnerable to some fairly trivial TOCTOU race conditions. [There
    14> are some Linux kernel patches I'm working on which might allow for a better
    15> solution.][lwn-obeneath]
    16>
    17> In addition, with a slightly modified API it might be possible to use
    18> `O_PATH` and verify that the opened path is actually the resolved one -- but
    19> I have not done that yet. I might add it in the future as a helper function
    20> to help users verify the path (we can't just return `/proc/self/fd/<foo>`
    21> because that doesn't always work transparently for all users).
    22
    23This is the function prototype:
    24
    25```go
    26func SecureJoin(root, unsafePath string) (string, error)
    27```
    28
    29This library **guarantees** the following:
    30
    31* If no error is set, the resulting string **must** be a child path of
    32  `root` and will not contain any symlink path components (they will all be
    33  expanded).
    34
    35* When expanding symlinks, all symlink path components **must** be resolved
    36  relative to the provided root. In particular, this can be considered a
    37  userspace implementation of how `chroot(2)` operates on file paths. Note that
    38  these symlinks will **not** be expanded lexically (`filepath.Clean` is not
    39  called on the input before processing).
    40
    41* Non-existent path components are unaffected by `SecureJoin` (similar to
    42  `filepath.EvalSymlinks`'s semantics).
    43
    44* The returned path will always be `filepath.Clean`ed and thus not contain any
    45  `..` components.
    46
    47A (trivial) implementation of this function on GNU/Linux systems could be done
    48with the following (note that this requires root privileges and is far more
    49opaque than the implementation in this library, and also requires that
    50`readlink` is inside the `root` path):
    51
    52```go
    53package securejoin
    54
    55import (
    56	"os/exec"
    57	"path/filepath"
    58)
    59
    60func SecureJoin(root, unsafePath string) (string, error) {
    61	unsafePath = string(filepath.Separator) + unsafePath
    62	cmd := exec.Command("chroot", root,
    63		"readlink", "--canonicalize-missing", "--no-newline", unsafePath)
    64	output, err := cmd.CombinedOutput()
    65	if err != nil {
    66		return "", err
    67	}
    68	expanded := string(output)
    69	return filepath.Join(root, expanded), nil
    70}
    71```
    72
    73[lwn-obeneath]: https://lwn.net/Articles/767547/
    74[go#20126]: https://github.com/golang/go/issues/20126
    75
    76### License ###
    77
    78The license of this project is the same as Go, which is a BSD 3-clause license
    79available in the `LICENSE` file.

View as plain text