fileutil

package
v0.68.4 Latest Latest
Warning

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

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

README

fileutil Package

The fileutil package provides utility functions for safe file path validation and common file operations.

Overview

This package focuses on security-conscious file handling: path validation, boundary enforcement, and straightforward file/directory operations. It also provides a cross-platform tar extraction helper.

Functions

Path Validation
ValidateAbsolutePath(path string) (string, error)

Validates that a file path is absolute and safe to use. The function:

  1. Rejects empty paths.
  2. Cleans the path with filepath.Clean to normalize . and .. components.
  3. Verifies the cleaned path is absolute.

Returns the cleaned absolute path on success, or an error otherwise. Use this before any file operation to defend against relative path traversal.

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

cleanPath, err := fileutil.ValidateAbsolutePath(userInput)
if err != nil {
    return fmt.Errorf("invalid path: %w", err)
}
content, err := os.ReadFile(cleanPath)
MustBeWithin(base, candidate string) error

Checks that candidate is located within the base directory tree. Both paths are resolved through filepath.EvalSymlinks (falling back to filepath.Abs for paths that do not yet exist on disk) before comparison, preventing both .. traversal and symlink escapes.

if err := fileutil.MustBeWithin("/workspace", outputPath); err != nil {
    return fmt.Errorf("output path escapes workspace: %w", err)
}
File and Directory Checks
FileExists(path string) bool

Returns true if path exists and is a regular file (not a directory).

DirExists(path string) bool

Returns true if path exists and is a directory.

IsDirEmpty(path string) bool

Returns true if the directory at path contains no entries. Returns true if the directory cannot be read.

File Operations
CopyFile(src, dst string) error

Copies the file at src to dst using buffered I/O. Creates dst if it does not exist; truncates it if it does. Calls Sync on the destination before closing.

if err := fileutil.CopyFile("source.txt", "destination.txt"); err != nil {
    return fmt.Errorf("copy failed: %w", err)
}
Archive Operations
ExtractFileFromTar(data []byte, path string) ([]byte, error)

Extracts a single file by path from a tar archive stored in data. Uses Go's archive/tar for cross-platform compatibility.

Security guarantees:

  • path must be a local, relative path (no .. components or absolute paths).
  • Individual tar entries with unsafe names are skipped, not extracted.
tarData, _ := io.ReadAll(response.Body)
content, err := fileutil.ExtractFileFromTar(tarData, "bin/gh")
if err != nil {
    return fmt.Errorf("binary not found in release archive: %w", err)
}

Design Notes

  • All debug output uses logger.New("fileutil:fileutil") and logger.New("fileutil:tar") and is only emitted when DEBUG=fileutil:*.
  • MustBeWithin resolves symlinks before comparison, providing defence-in-depth against symlink attacks in addition to the .. checking that ValidateAbsolutePath provides.
  • ExtractFileFromTar rejects path-traversal payloads in both the caller-supplied path and in tar entry names using filepath.IsLocal.

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 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 MustBeWithin added in v0.64.1

func MustBeWithin(base, candidate string) error

MustBeWithin 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.

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)

Types

This section is empty.

Jump to

Keyboard shortcuts

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