Safely join paths to prevent directory traversal in Go.
When accepting user-controlled relative paths and joining them to a base
directory, naive filepath.Join(base, rel) can produce a path outside base
via ../ traversal. This is a common security vulnerability.
pathsafe.SafeJoin resolves this by:
- Resolving
baseto an absolute, cleaned path. - Joining
reland cleaning the result. - Verifying the result is within base (equal to base or a subpath).
- Returning
ErrOutsideBaseif traversal is detected.
import "github.com/azghr/forge/pathsafe"
safe, err := pathsafe.SafeJoin("/home/user", "docs/report.pdf")
// safe == "/home/user/docs/report.pdf"
_, err = pathsafe.SafeJoin("/home/user", "../etc/passwd")
// err == pathsafe.ErrOutsideBaseSafeJoin(base, rel string, opts ...Option) (string, error)– joins base and rel, ensuring the result is within base. Returns cleaned absolute path orErrOutsideBase. Options can enable symlink resolution.
AllowSymlinkFollow()– resolves symlinks in both base and joined paths before the containment check. Use this to prevent symlink-based traversal attacks.
ErrOutsideBase– returned when the joined path is outside the base directory. Useerrors.Is(err, pathsafe.ErrOutsideBase)to check.
- O(path length) – uses
filepath.Abs,filepath.Clean, and optionallyfilepath.EvalSymlinks. - No allocations beyond the input string lengths and filesystem calls.
- Concurrency-safe: zero global state, no shared mutexes.