package securejoin
import "github.com/cyphar/filepath-securejoin"
Package securejoin implements a set of helpers to make it easier to write Go code that is safe against symlink-related escape attacks. The primary idea is to let you resolve a path within a rootfs directory as if the rootfs was a chroot.
securejoin has two APIs, a "legacy" API and a "modern" API.
The legacy API is SecureJoin and SecureJoinVFS. These methods are **not** safe against race conditions where an attacker changes the filesystem after (or during) the SecureJoin operation.
The new API is made up of [OpenInRoot] and [MkdirAll] (and derived functions). These are safe against racing attackers and have several other protections that are not provided by the legacy API. There are many more operations that most programs expect to be able to do safely, but we do not provide explicit support for them because we want to encourage users to switch to [libpathrs](https://github.com/openSUSE/libpathrs) which is a cross-language next-generation library that is entirely designed around operating on paths safely.
securejoin has been used by several container runtimes (Docker, runc, Kubernetes, etc) for quite a few years as a de-facto standard for operating on container filesystem paths "safely". However, most users still use the legacy API which is unsafe against various attacks (there is a fairly long history of CVEs in dependent as a result). Users should switch to the modern API as soon as possible (or even better, switch to libpathrs).
This project was initially intended to be included in the Go standard library, but [it was rejected](https://go.dev/issue/20126). There is now a [new Go proposal](https://go.dev/issue/67002) for a safe path resolution API that shares some of the goals of filepath-securejoin. However, that design is intended to work like `openat2(RESOLVE_BENEATH)` which does not fit the usecase of container runtimes and most system tools.
Index ¶
- func IsNotExist(err error) bool
- func SecureJoin(root, unsafePath string) (string, error)
- func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error)
- type VFS
Functions ¶
func IsNotExist ¶
IsNotExist tells you if err is an error that implies that either the path accessed does not exist (or path components don't exist). This is effectively a more broad version of os.IsNotExist.
func SecureJoin ¶
SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library of functions as the VFS. If in doubt, use this function over SecureJoinVFS.
func SecureJoinVFS ¶
SecureJoinVFS joins the two given path components (similar to filepath.Join) except that the returned path is guaranteed to be scoped inside the provided root path (when evaluated). Any symbolic links in the path are evaluated with the given root treated as the root of the filesystem, similar to a chroot. The filesystem state is evaluated through the given VFS interface (if nil, the standard os.* family of functions are used).
Note that the guarantees provided by this function only apply if the path components in the returned string are not modified (in other words are not replaced with symlinks on the filesystem) after this function has returned. Such a symlink race is necessarily out-of-scope of SecureJoinVFS.
NOTE: Due to the above limitation, Linux users are strongly encouraged to use [OpenInRoot] instead, which does safely protect against these kinds of attacks. There is no way to solve this problem with SecureJoinVFS because the API is fundamentally wrong (you cannot return a "safe" path string and guarantee it won't be modified afterwards).
Volume names in unsafePath are always discarded, regardless if they are provided via direct input or when evaluating symlinks. Therefore:
"C:\Temp" + "D:\path\to\file.txt" results in "C:\Temp\path\to\file.txt"
If the provided root is not filepath.Clean then an error will be returned, as such root paths are bordering on somewhat unsafe and using such paths is not best practice. We also strongly suggest that any root path is first fully resolved using filepath.EvalSymlinks or otherwise constructed to avoid containing symlink components. Of course, the root also *must not* be attacker-controlled.
Types ¶
type VFS ¶
type VFS interface { // Lstat returns an [os.FileInfo] describing the named file. If the // file is a symbolic link, the returned [os.FileInfo] describes the // symbolic link. Lstat makes no attempt to follow the link. // The semantics are identical to [os.Lstat]. Lstat(name string) (os.FileInfo, error) // Readlink returns the destination of the named symbolic link. // The semantics are identical to [os.Readlink]. Readlink(name string) (string, error) }
VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is equivalent to using the standard os.* family of functions. This is mainly used for the purposes of mock testing, but also can be used to otherwise use SecureJoinVFS with VFS-like system.
Source Files ¶
- Version
- v0.4.1 (latest)
- Published
- Jan 28, 2025
- Platform
- js/wasm
- Imports
- 5 packages
- Last checked
- 4 weeks ago –
Tools for package owners.