safefile

package
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 24, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
var ErrFileExists = errors.E(errors.InvalidInput, "file already exists", nil)

ErrFileExists is returned when WriteFileExclusive encounters an existing file.

Functions

func ContainsSymlink(path string) (bool, error)

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

func ContainsSymlinkFrom(path, root string) (bool, error)

ContainsSymlinkFrom checks if any component in the path from root is a symlink. Only checks path components between root and path.

func MkdirAll

func MkdirAll(baseDir, targetDir string) error

MkdirAll creates a directory and all parents with symlink protection. Uses standard directory permissions (0755).

func MkdirAllPrivate

func MkdirAllPrivate(baseDir, targetDir string) error

MkdirAllPrivate creates directories with restrictive permissions (0700).

func OpenForRead

func OpenForRead(path string) (*os.File, error)

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

func OpenForWrite(path string) (*os.File, error)

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

func ReadFile(path string, limit limits.SizeLimit) ([]byte, error)

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

func Rename(baseDir, srcPath, dstPath string) error

Rename atomically moves a file, creating destination directories as needed. Both source and destination must be under baseDir.

func ValidatePath

func ValidatePath(baseDir, relPath string) (string, error)

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

func ValidateRegularFile(root, relPath string) (string, error)

ValidateRegularFile validates that relPath is a regular file within root. Returns the absolute path if valid. Rejects symlinks and non-regular files.

func WriteFile

func WriteFile(baseDir, path string, data []byte) error

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

func WriteFileExclusive(baseDir, path string, data []byte) error

WriteFileExclusive writes data, failing if the file already exists. Use when overwriting would indicate a bug or race condition.

func WriteFilePrivate

func WriteFilePrivate(baseDir, path string, data []byte) error

WriteFilePrivate writes data with restrictive permissions (0700/0600). Use for credentials, keys, tokens, or other sensitive data.

func WriteJSON

func WriteJSON(baseDir, path string, v any) error

WriteJSON marshals v to indented JSON and writes atomically.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL