execsafe

package
v0.8.0 Latest Latest
Warning

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

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

Documentation

Overview

Package execsafe provides TOCTOU-safe binary execution primitives. These are used by both collector and tool execution to ensure cryptographic verification of binaries before execution.

Index

Constants

This section is empty.

Variables

View Source
var AllowedEnvVars = []string{

	"HOME",
	"USER",

	"LANG",
	"LC_ALL",
	"LC_CTYPE",

	"TZ",

	"TMPDIR",
	"TEMP",
	"TMP",

	"TERM",
	"NO_COLOR",
	"CLICOLOR",
	"CLICOLOR_FORCE",

	"SSL_CERT_FILE",
	"SSL_CERT_DIR",

	"HTTP_PROXY",
	"HTTPS_PROXY",
	"NO_PROXY",
	"http_proxy",
	"https_proxy",
	"no_proxy",

	"XDG_CONFIG_HOME",
	"XDG_CACHE_HOME",
	"XDG_DATA_HOME",
	"XDG_STATE_HOME",
	"XDG_RUNTIME_DIR",
}

AllowedEnvVars is the allowlist of environment variables passed to collectors and tools. This prevents credential exfiltration via malicious/compromised binaries. NOTE: PATH is handled separately via SafePATH() - it is NOT in this list.

View Source
var DeniedSecretPrefixes = []string{"EPACK_", "LD_", "DYLD_", "_"}

DeniedSecretPrefixes blocks variables that would compromise epack's execution. - EPACK_*: Protocol namespace (run_id, pack_path, etc.) - LD_*/DYLD_*: Dynamic linker hijacking - _*: Reserved by shells/runtimes

Functions

func AppendAllowedSecrets

func AppendAllowedSecrets(dst []string, names []string, getenv func(string) string) []string

AppendAllowedSecrets appends allowed secrets from the environment to dst. Only secrets that pass validation and have non-empty values are appended. The getenv function is typically os.Getenv but can be mocked for testing.

SECURITY: This is the canonical way to pass secrets to subprocesses. It filters reserved prefixes and only passes explicitly configured secrets.

func BuildRestrictedEnv

func BuildRestrictedEnv(environ []string, inheritPath bool) []string

BuildRestrictedEnv builds a restricted environment for binary execution. SECURITY: By default, PATH is set to a safe, deterministic value to prevent PATH injection attacks. Only with inheritPath=true is the ambient PATH inherited (which may contain attacker-controlled directories).

func BuildRestrictedEnvSafe

func BuildRestrictedEnvSafe(environ []string, inheritPath bool) []string

BuildRestrictedEnvSafe builds a restricted environment with proxy credentials stripped. This is the recommended function for executing untrusted binaries.

SECURITY: This function: 1. Filters to only allowed environment variables 2. Strips credentials from proxy URLs 3. Sets a safe, deterministic PATH (unless inheritPath is true)

func FilterEnv

func FilterEnv(environ []string, allowed []string) []string

FilterEnv returns only the allowed environment variables.

func FilterValidSecrets

func FilterValidSecrets(secrets []string) []string

FilterValidSecrets returns only secrets that pass validation.

func FstatInode

func FstatInode(fd int) (uint64, error)

FstatInode returns the inode of a file descriptor.

func OpenBinaryNoFollow

func OpenBinaryNoFollow(path string) (int, error)

OpenBinaryNoFollow opens a binary with O_NOFOLLOW to prevent symlink attacks. Returns the fd and an error.

func SafePATH

func SafePATH() string

SafePATH returns a deterministic, minimal PATH for binary execution. This prevents PATH injection attacks where a malicious interpreter is placed earlier in PATH than the legitimate one.

SECURITY: On Windows, we use hardcoded paths rather than environment variables. The SystemRoot env var can be controlled by attackers. We use the standard Windows directory path. If Windows is installed elsewhere, the PATH will be wrong but the operation will fail safely (command not found) rather than executing attacker binaries.

func SecureTempDir

func SecureTempDir(prefix string) (string, func(), error)

SecureTempDir creates a temporary directory with restrictive permissions. Uses umask to ensure the directory is CREATED with 0700 permissions, eliminating the race window between MkdirTemp and Chmod.

func StripProxyCredentials

func StripProxyCredentials(environ []string) []string

StripProxyCredentials removes username:password from proxy environment variables. This prevents credential leakage to untrusted binaries while still allowing proxy connectivity.

SECURITY: Proxy URLs can contain embedded credentials like:

http://user:[email protected]:8080

These credentials would be visible to any executed binary. This function strips credentials while preserving the proxy host:port for connectivity.

Returns the sanitized environment variables.

func ValidateSecretName

func ValidateSecretName(name string) error

ValidateSecretName returns an error if the name is a denied prefix.

func ValidateSecretNames

func ValidateSecretNames(names []string) error

ValidateSecretNames validates a list of secret names.

func VerifiedBinaryFD

func VerifiedBinaryFD(binaryPath, expectedDigest string) (execPath string, cleanup func(), err error)

VerifiedBinaryFD creates a verified, sealed copy of a binary for execution. This eliminates TOCTOU races by hashing bytes AS they are copied to the sealed temp file - we execute exactly the bytes we verified.

SECURITY: The critical invariant is that we never execute bytes we didn't hash. Previous approaches that hash-then-copy or hash-then-exec-fd are vulnerable to race conditions where the source file is modified between operations.

The approach: 1. Open the source binary with O_RDONLY | O_NOFOLLOW (prevents symlink following) 2. Create a sealed temp file in a temp directory 3. Copy bytes through a TeeReader that simultaneously hashes and writes 4. Verify the hash matches AFTER the copy completes 5. Seal the temp directory and return the path to the verified copy

This is race-free because the bytes written to the temp file ARE the bytes that were hashed - there's no window for modification.

func VerifyDigestFromFD

func VerifyDigestFromFD(fd int, expectedDigest string) error

VerifyDigestFromFD verifies a file's digest by reading from an already-open fd. This is used for TOCTOU-safe verification where the caller already has an open fd.

func WriteSecureConfigFile

func WriteSecureConfigFile(config map[string]interface{}, prefix string) (string, func(), error)

WriteSecureConfigFile writes config data to a secure temporary JSON file. This is the standard pattern for passing config to collectors and tools.

SECURITY: Uses SecureTempDir for race-free restrictive permissions. The returned cleanup function removes both the file and its parent directory.

Returns empty string and nil cleanup if config is empty.

Types

type CommandOptions

type CommandOptions struct {
	// InheritPath uses the ambient PATH instead of SafePATH.
	// SECURITY WARNING: Only use this for trusted binaries where PATH
	// injection is not a concern (e.g., system tools during build).
	InheritPath bool

	// StripProxyCredentials removes username:password from proxy env vars.
	// Defaults to true for untrusted binaries.
	StripProxyCredentials bool

	// AdditionalEnv adds extra environment variables to the base restricted set.
	// These are added AFTER filtering, so they can include any variables.
	// SECURITY: The caller must validate these do not contain secrets.
	AdditionalEnv []string

	// Secrets is a list of environment variable names to pass through.
	// Each name is validated against DeniedSecretPrefixes before lookup.
	// Only secrets that exist in the environment AND pass validation are included.
	Secrets []string

	// WorkDir sets the working directory for command execution.
	WorkDir string
}

CommandOptions configures how a restricted command is built.

func DefaultCommandOptions

func DefaultCommandOptions() CommandOptions

DefaultCommandOptions returns secure defaults for command construction.

type RestrictedCommand

type RestrictedCommand struct {
	// Path is the executable path (may be a verified copy from VerifiedBinaryFD).
	Path string

	// Args is the argument list (Args[0] should be the command name).
	Args []string

	// Env is the security-filtered environment.
	Env []string

	// Dir is the working directory (empty means current directory).
	Dir string
	// contains filtered or unexported fields
}

RestrictedCommand holds a prepared command with security-validated environment. Use NewRestrictedCommand to create instances.

func NewRestrictedCommand

func NewRestrictedCommand(binaryPath string, args []string, opts CommandOptions) (*RestrictedCommand, error)

NewRestrictedCommand creates a command with security-hardened environment. This is the REQUIRED entry point for executing collectors and tools.

SECURITY: This function centralizes all security checks for command execution:

  • Environment filtering to AllowedEnvVars only
  • Safe PATH (no user-controlled directories)
  • Secret name validation (blocks EPACK_*, LD_*, DYLD_*, _*)
  • Optional proxy credential stripping

Example usage:

cmd, err := execsafe.NewRestrictedCommand(binaryPath, args, execsafe.DefaultCommandOptions())
if err != nil {
    return err
}
defer cmd.Cleanup()

execCmd := exec.Command(cmd.Path, cmd.Args[1:]...)
execCmd.Env = cmd.Env
execCmd.Dir = cmd.Dir
return execCmd.Run()

func NewVerifiedRestrictedCommand

func NewVerifiedRestrictedCommand(binaryPath, expectedDigest string, args []string, opts CommandOptions) (*RestrictedCommand, error)

NewVerifiedRestrictedCommand creates a command with both verified binary and security-hardened environment.

SECURITY: This function provides the strongest security guarantees:

  • Binary is verified against expected digest (TOCTOU-safe)
  • Environment is filtered and sanitized
  • A verified copy of the binary is executed

The caller MUST call Cleanup() after execution to remove the verified copy.

Example usage:

cmd, err := execsafe.NewVerifiedRestrictedCommand(
    binaryPath, expectedDigest, args, execsafe.DefaultCommandOptions())
if err != nil {
    return err
}
defer cmd.Cleanup()

execCmd := exec.Command(cmd.Path, cmd.Args[1:]...)
execCmd.Env = cmd.Env
return execCmd.Run()

func (*RestrictedCommand) Cleanup

func (c *RestrictedCommand) Cleanup()

Cleanup releases resources associated with this command. Safe to call multiple times or on nil.

Jump to

Keyboard shortcuts

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