fileutil

package
v0.80.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 8 Imported by: 0

README

fileutil Package

Utility functions for security-conscious file path validation and common file operations.

Overview

The fileutil package focuses on security-conscious file handling: path validation, directory-boundary enforcement, and straightforward file and directory operations. It also provides a cross-platform tar extraction helper that guards against path-traversal payloads embedded in archives.

All functions emit debug output only when DEBUG=fileutil:* is active, and never write to stdout.

Public API

Functions
Function Signature Description
ValidateAbsolutePath func(path string) (string, error) Validates that a file path is absolute and safe; rejects empty paths, cleans with filepath.Clean, and verifies the result is absolute
ValidatePathWithinBase func(base, candidate string) error Checks that candidate is located within the base directory tree; resolves symlinks before comparison to prevent traversal and symlink escapes
FileExists func(path string) bool Returns true if path exists and is a regular file (not a directory)
DirExists func(path string) bool Returns true if path exists and is a directory
IsDirEmpty func(path string) bool Returns true if the directory at path contains no entries; also returns true if the directory cannot be read
CopyFile func(src, dst string) error Copies the file at src to dst using buffered I/O; calls Sync on the destination before closing; removes the partial destination file on write error
EnsureParentDir func(path string, perm os.FileMode) error Ensures the parent directory of path exists, creating it recursively with the given permissions; returns an error for empty paths
ExtractFileFromTar func(data []byte, path string) ([]byte, error) Extracts a single file by path from a tar archive; rejects unsafe entry names (absolute or ..-containing paths) using filepath.IsLocal

Behavioral contracts:

  • ValidateAbsolutePath MUST reject empty paths and MUST return an error for any path that is not absolute after filepath.Clean.
  • ValidatePathWithinBase MUST resolve symlinks via filepath.EvalSymlinks (falling back to filepath.Abs for non-existent paths) before comparing, so neither .. components nor symlinks pointing outside base can escape the boundary.
  • IsDirEmpty MUST return true when the directory cannot be read (treats unreadable as empty).
  • CopyFile MUST call Sync on the destination before closing, and MUST remove the partial destination file if a write error occurs mid-copy.
  • ExtractFileFromTar MUST reject the caller-supplied path and each tar entry name that does not satisfy filepath.IsLocal; returns an error when the requested file is not present in the archive.

Usage Examples

import "github.com/github/gh-aw/pkg/fileutil"

// Validate and clean a user-supplied path
cleanPath, err := fileutil.ValidateAbsolutePath(userInput)
if err != nil {
    return fmt.Errorf("invalid path: %w", err)
}

// Ensure output path stays within workspace
if err := fileutil.ValidatePathWithinBase("/workspace", outputPath); err != nil {
    return fmt.Errorf("output path escapes workspace: %w", err)
}

// Copy a file (partial destination is removed on error)
if err := fileutil.CopyFile("source.txt", "destination.txt"); err != nil {
    return fmt.Errorf("copy failed: %w", err)
}

// Extract a file from a tar archive in memory
content, err := fileutil.ExtractFileFromTar(tarBytes, "dist/binary")
if err != nil {
    return fmt.Errorf("extraction failed: %w", err)
}

Thread Safety

All exported functions are safe for concurrent use. None of them share mutable package-level state; the package-level logger variables (fileutilLog, tarLog) are read-only after package initialization.

Dependencies

Internal:

  • github.com/github/gh-aw/pkg/logger — debug logging

Design Decisions

  • ValidatePathWithinBase resolves symlinks before comparison, providing defence-in-depth against symlink attacks in addition to the .. checking that ValidateAbsolutePath provides.
  • ExtractFileFromTar uses Go's standard archive/tar instead of an external tar process, ensuring cross-platform compatibility in environments where tar may not be on PATH.
  • CopyFile removes the partial destination file on write error to prevent leaving corrupt files behind.
  • All debug output uses the logger package and is only emitted when DEBUG=fileutil:*.

This specification is automatically maintained by the spec-extractor workflow.

Documentation

Overview

Package fileutil provides utility functions for working with file paths and file operations.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CopyFile added in v0.47.0

func CopyFile(src, dst string) error

CopyFile copies a file from src to dst using buffered IO.

func DirExists added in v0.47.0

func DirExists(path string) bool

DirExists checks if a directory exists.

func EnsureParentDir added in v0.79.3

func EnsureParentDir(path string, perm os.FileMode) error

EnsureParentDir ensures the parent directory for path exists, creating it recursively when needed.

func ExtractFileFromTar added in v0.49.0

func ExtractFileFromTar(data []byte, path string) ([]byte, error)

ExtractFileFromTar extracts a single file from a tar archive. Uses Go's standard archive/tar for cross-platform compatibility instead of spawning an external tar process which may not be available on all platforms.

path must be a local, relative path (no absolute paths or ".." components). filepath.IsLocal is used to enforce this for both the search target and each tar entry name, guarding against path-traversal payloads embedded in archives.

func FileExists added in v0.47.0

func FileExists(path string) bool

FileExists checks if a file exists and is not a directory.

func IsDirEmpty added in v0.47.0

func IsDirEmpty(path string) bool

IsDirEmpty checks if a directory is empty.

func ValidateAbsolutePath

func ValidateAbsolutePath(path string) (string, error)

ValidateAbsolutePath validates that a file path is absolute and safe to use. It performs the following security checks:

  • Cleans the path using filepath.Clean to normalize . and .. components
  • Verifies the path is absolute to prevent relative path traversal attacks

Returns the cleaned absolute path if validation succeeds, or an error if:

  • The path is empty
  • The path is relative (not absolute)

This function should be used before any file operations (read, write, stat, etc.) to ensure defense-in-depth security against path traversal vulnerabilities.

Example:

cleanPath, err := fileutil.ValidateAbsolutePath(userInputPath)

if err != nil {
   return fmt.Errorf("invalid path: %w", err)
}

content, err := os.ReadFile(cleanPath)

func ValidatePathWithinBase added in v0.71.5

func ValidatePathWithinBase(base, candidate string) error

ValidatePathWithinBase checks that candidate is located within the base directory tree. Both paths are resolved via filepath.EvalSymlinks (with filepath.Abs as fallback when a path does not yet exist) before comparison, so neither ".." components nor symlinks pointing outside base can be used to escape.

Returns an error when:

  • Either path cannot be resolved to an absolute form.
  • The resolved candidate path starts outside the resolved base directory.

Types

This section is empty.

Jump to

Keyboard shortcuts

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