Documentation
¶
Overview ¶
Package allowedpaths implements a filesystem sandbox that restricts access to a set of allowed directories using os.Root (Go 1.24+).
Index ¶
- Constants
- func CollectDirEntries(readBatch func(n int) ([]fs.DirEntry, error), batchSize, offset, maxRead int) ([]fs.DirEntry, bool, error)
- func FileIdentity(_ string, info fs.FileInfo, _ *Sandbox) (uint64, uint64, bool)
- func IsDevNull(path string) bool
- func IsErrIsDirectory(err error) bool
- func PortableErrMsg(err error) string
- func PortablePathError(err error) error
- type Sandbox
- func (s *Sandbox) Access(path string, cwd string, mode uint32) error
- func (s *Sandbox) Close() error
- func (s *Sandbox) IsDirEmpty(path string, cwd string) (bool, error)
- func (s *Sandbox) Lstat(path string, cwd string) (fs.FileInfo, error)
- func (s *Sandbox) Open(path string, cwd string, flag int, perm os.FileMode) (io.ReadWriteCloser, error)
- func (s *Sandbox) OpenDir(path string, cwd string) (fs.ReadDirFile, error)
- func (s *Sandbox) ReadDir(path string, cwd string) ([]fs.DirEntry, error)
- func (s *Sandbox) ReadDirForGlob(path string, cwd string) ([]fs.DirEntry, error)
- func (s *Sandbox) ReadDirLimited(path string, cwd string, offset, maxRead int) ([]fs.DirEntry, bool, error)
- func (s *Sandbox) Stat(path string, cwd string) (fs.FileInfo, error)
Constants ¶
const MaxGlobEntries = 100_000
MaxGlobEntries is the maximum number of directory entries read per single glob expansion step. ReadDirForGlob returns an error for directories that exceed this limit to prevent memory exhaustion during pattern matching.
Variables ¶
This section is empty.
Functions ¶
func CollectDirEntries ¶
func CollectDirEntries(readBatch func(n int) ([]fs.DirEntry, error), batchSize, offset, maxRead int) ([]fs.DirEntry, bool, error)
CollectDirEntries reads directory entries in batches using readBatch, skipping the first offset entries and collecting up to maxRead entries. Returns (entries, truncated, lastErr). Entries are sorted by name.
NOTE: We intentionally truncate before reading all entries. For directories larger than maxRead, the returned entries are sorted within the read window but may not be the globally-smallest names. Reading all entries to get globally-correct sorting would defeat the DoS protection — a directory with millions of files would OOM or stall. The truncation warning communicates that output is incomplete.
func FileIdentity ¶
FileIdentity extracts canonical file identity (dev+inode) from FileInfo. On Unix, this is extracted directly from Stat_t via info.Sys().
func IsErrIsDirectory ¶
IsErrIsDirectory reports whether err is an "is a directory" error.
func PortableErrMsg ¶
PortableErrMsg returns a POSIX-style error message for the given error, normalizing platform-specific syscall messages to consistent strings. This ensures shell error output is identical across Linux, macOS, and Windows.
func PortablePathError ¶
PortablePathError returns a *os.PathError with a normalized error message. If the error is not a *os.PathError, it is returned as-is. Only the Err field is normalized; the Path and Op fields are preserved as-is.
Types ¶
type Sandbox ¶
type Sandbox struct {
// contains filtered or unexported fields
}
Sandbox restricts filesystem access to a set of allowed directories. The restriction is enforced using os.Root (Go 1.24+), which uses openat syscalls for atomic path validation — immune to symlink and ".." traversal attacks.
func New ¶
New validates paths and eagerly opens os.Root handles so the allowed directories are pinned before the caller can modify them between construction and the first run.
func (*Sandbox) Access ¶
Access checks whether the resolved path is accessible with the given mode. All operations go through os.Root to stay within the sandbox. Mode: 0x04 = read, 0x02 = write, 0x01 = execute.
func (*Sandbox) Close ¶
Close releases all os.Root file descriptors. It is safe to call multiple times.
func (*Sandbox) IsDirEmpty ¶
IsDirEmpty checks whether a directory is empty by reading at most one entry. More efficient than reading all entries when only emptiness needs to be determined.
func (*Sandbox) Lstat ¶
Lstat implements the restricted lstat policy. Like stat, it uses a metadata-only call, but does not follow symbolic links — the returned FileInfo describes the link itself rather than its target.
func (*Sandbox) Open ¶
func (s *Sandbox) Open(path string, cwd string, flag int, perm os.FileMode) (io.ReadWriteCloser, error)
Open implements the restricted file-open policy. The file is opened through os.Root for atomic path validation. Only read-only access is permitted; any write flags are rejected as a defense-in-depth measure.
func (*Sandbox) OpenDir ¶
OpenDir opens a directory within the sandbox for incremental reading via ReadDir(n). The caller must close the returned handle when done. Returns fs.ReadDirFile to expose only read-only directory methods.
func (*Sandbox) ReadDirForGlob ¶
ReadDirForGlob reads directory entries for glob expansion, capped at MaxGlobEntries. The underlying ReadDir call is limited to MaxGlobEntries+1 so the kernel never materialises more entries than needed. If the directory exceeds the limit an error is returned before any pattern matching or sorting can occur, making the failure explicit rather than silently returning a partial listing that could miss valid matches.
func (*Sandbox) ReadDirLimited ¶
func (s *Sandbox) ReadDirLimited(path string, cwd string, offset, maxRead int) ([]fs.DirEntry, bool, error)
ReadDirLimited reads directory entries, skipping the first offset entries and returning up to maxRead entries sorted by name within the read window. Returns (entries, truncated, error). When truncated is true, the directory contained more entries beyond the returned set.
The offset skips raw directory entries during reading (before sorting). This means offset does NOT correspond to positions in a sorted listing — pages may overlap or miss entries. This is an acceptable tradeoff to achieve O(n) memory regardless of offset value, where n = min(maxRead, entries).