Documentation
¶
Overview ¶
Package safefile provides secure filesystem operations with symlink protection.
This is the primary package for all file I/O in security-sensitive contexts. It combines path validation, symlink protection, and atomic operations.
Which Function Should I Use? ¶
Writing files:
safefile.WriteFile(baseDir, path, data) // Default: creates dirs, atomic, 0755/0644 safefile.WriteFilePrivate(baseDir, path, data) // For secrets: 0700/0600 permissions safefile.WriteFileExclusive(baseDir, path, data) // Fail if file already exists safefile.WriteJSON(baseDir, path, v) // Marshal and write atomically
Reading files:
safefile.ReadFile(path, limits.ConfigFile) // With size limit, refuses symlinks
Streaming (when you need an io.Reader/Writer):
safefile.OpenForRead(path) // Returns *os.File, refuses symlinks safefile.OpenForWrite(path) // Returns *os.File, refuses symlinks
Directories:
safefile.MkdirAll(baseDir, path) // Create with symlink protection
Path validation (pure string operations, no I/O):
safefile.ValidatePath(baseDir, relPath) // Check path stays within baseDir safefile.ContainsSymlink(path) // Check if any component is a symlink
Security Properties ¶
All operations in this package:
- Refuse to follow symlinks (using O_NOFOLLOW on Unix)
- Validate path containment to prevent directory traversal
- Use atomic operations where possible (temp file + rename)
- Pin file descriptors to prevent TOCTOU race conditions
Base Directory ¶
Most write operations require a baseDir parameter. This is the trusted root directory that the operation must stay within. All paths are validated to ensure they don't escape this directory via ".." or symlinks.
Index ¶
- Variables
- func ContainsSymlink(path string) (bool, error)
- func ContainsSymlinkFrom(path, root string) (bool, error)
- func MkdirAll(baseDir, targetDir string) error
- func MkdirAllPrivate(baseDir, targetDir string) error
- func OpenForRead(path string) (*os.File, error)
- func OpenForWrite(path string) (*os.File, error)
- func ReadFile(path string, limit limits.SizeLimit) ([]byte, error)
- func Rename(baseDir, srcPath, dstPath string) error
- func ValidatePath(baseDir, relPath string) (string, error)
- func ValidateRegularFile(root, relPath string) (string, error)
- func WriteFile(baseDir, path string, data []byte) error
- func WriteFileExclusive(baseDir, path string, data []byte) error
- func WriteFilePrivate(baseDir, path string, data []byte) error
- func WriteJSON(baseDir, path string, v any) error
Constants ¶
This section is empty.
Variables ¶
var ErrFileExists = errors.E(errors.InvalidInput, "file already exists", nil)
ErrFileExists is returned when WriteFileExclusive encounters an existing file.
Functions ¶
func ContainsSymlink ¶
ContainsSymlink checks if any component in the path from root is a symlink. If root is empty, checks from filesystem root. Returns true if a symlink is found, false otherwise.
func ContainsSymlinkFrom ¶
ContainsSymlinkFrom checks if any component in the path from root is a symlink. Only checks path components between root and path.
func MkdirAll ¶
MkdirAll creates a directory and all parents with symlink protection. Uses standard directory permissions (0755).
func MkdirAllPrivate ¶
MkdirAllPrivate creates directories with restrictive permissions (0700).
func OpenForRead ¶
OpenForRead opens a file for reading, refusing symlinks. Returns an *os.File that the caller must close. Use for streaming reads when ReadFile's size limit is not appropriate.
func OpenForWrite ¶
OpenForWrite opens a file for writing, refusing symlinks. Returns an *os.File that the caller must close. Use for streaming writes when WriteFile is not appropriate.
func ReadFile ¶
ReadFile reads a file with size limit enforcement and symlink protection.
SECURITY:
- Uses O_NOFOLLOW to atomically refuse symlinks
- Validates size before reading to prevent memory exhaustion
- Uses fstat on open fd to avoid TOCTOU races
func Rename ¶
Rename atomically moves a file, creating destination directories as needed. Both source and destination must be under baseDir.
func ValidatePath ¶
ValidatePath validates that relPath stays within baseDir. Returns the absolute path if valid.
This is a pure string operation - it does not access the filesystem. Use this when you need path validation without file I/O.
func ValidateRegularFile ¶
ValidateRegularFile validates that relPath is a regular file within root. Returns the absolute path if valid. Rejects symlinks and non-regular files.
func WriteFile ¶
WriteFile writes data atomically with symlink protection. Creates parent directories as needed. Uses standard permissions (0755/0644).
This is the default choice for writing files securely.
func WriteFileExclusive ¶
WriteFileExclusive writes data, failing if the file already exists. Use when overwriting would indicate a bug or race condition.
func WriteFilePrivate ¶
WriteFilePrivate writes data with restrictive permissions (0700/0600). Use for credentials, keys, tokens, or other sensitive data.
Types ¶
This section is empty.