shellwrap

package
v0.26.3 Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package shellwrap provides platform-level helpers for wrapping commands in the user's login shell and for resolving tool binaries (e.g. docker) with PATH caching.

It exists so that both the upstream proxy code (internal/upstream/core) and the security scanner (internal/security/scanner) can share a single, well-tested implementation of shell quoting + login-shell wrapping instead of each rolling their own.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LoginShellPATH added in v0.24.2

func LoginShellPATH(logger *zap.Logger) string

LoginShellPATH returns the PATH value emitted by the user's login shell. It is captured exactly once per process via `<shell> -l -c 'printf %s "$PATH"'` and cached for the rest of the process lifetime.

Why this exists: when mcpproxy runs as a macOS App Bundle or LaunchAgent, os.Getenv("PATH") is often `/usr/bin:/bin`. That is enough for Go's exec.LookPath to find a docker binary once shellwrap.ResolveDockerPath has cached its absolute path, but it is NOT enough for the docker CLI itself, which re-execs credential helpers like `docker-credential-desktop` via its own $PATH lookup. Those helpers typically live in /usr/local/bin or /opt/homebrew/bin — directories that only exist in the interactive login PATH.

On Windows, this function returns "" (credential-helper PATH drift is not the same problem there, and interactive-shell PATH capture would require cmd.exe or PowerShell gymnastics we explicitly avoid).

Callers should treat an empty return value as "no override available" and fall back to os.Getenv("PATH").

func MinimalEnv

func MinimalEnv() []string

MinimalEnv returns a minimal, allow-listed environment suitable for subprocesses that must NOT inherit the user's ambient credentials (e.g. AWS_ACCESS_KEY_ID, GITHUB_TOKEN, etc). It includes PATH + HOME on Unix and PATH + USERPROFILE on Windows so that `docker` itself still functions.

Callers that need TLS or Docker-specific variables (DOCKER_HOST, DOCKER_CONFIG, …) should append them explicitly.

On Unix, PATH is built by merging the user's login-shell PATH (captured once via LoginShellPATH) with the process's ambient PATH. Login-shell entries come first so that docker's own credential-helper lookups can find binaries installed in /opt/homebrew/bin or /usr/local/bin even when mcpproxy was started from a LaunchAgent with a minimal inherited PATH. See issue #381.

func MinimalEnvWithLogger added in v0.24.2

func MinimalEnvWithLogger(logger *zap.Logger) []string

MinimalEnvWithLogger is MinimalEnv with an optional logger used while capturing the login-shell PATH on the first call. Subsequent calls return the cached value without logging.

func ResolveDockerPath

func ResolveDockerPath(logger *zap.Logger) (string, error)

ResolveDockerPath returns the absolute path to the `docker` binary. Successful resolutions are cached for the process lifetime; failed resolutions are cached only for dockerPathNegativeTTL so a transient failure (e.g. PKInstallSandbox at process start) does not permanently disable docker discovery for the daemon.

Resolution order:

  1. exec.LookPath("docker") — cheap, works when mcpproxy was started from a terminal or when launchd's PATH already contains docker.
  2. Probe well-known install locations directly (Docker Desktop's bundle binary, /usr/local/bin/docker symlink, Apple Silicon Homebrew, ~/.docker/bin, OrbStack, snap, etc.). Avoids the fragile login-shell dance when the binary is at a predictable path.
  3. Last resort: ask the user's login shell `command -v docker` so we pick up Colima or other non-standard installs only present in the interactive PATH. Skipped on Windows.

func Shellescape

func Shellescape(s string) string

Shellescape escapes a single argument for safe inclusion in a shell command string. On Unix it uses POSIX single-quoting; on Windows it performs a best-effort cmd.exe quoting.

This mirrors the implementation in internal/upstream/core so both code paths can converge on one function.

func WrapWithUserShell

func WrapWithUserShell(logger *zap.Logger, command string, args []string) (shell string, shellArgs []string)

WrapWithUserShell wraps a command and its arguments in the user's login shell so the child process inherits the interactive PATH (important when mcpproxy is launched from a GUI / LaunchAgent on macOS).

It returns the shell to exec and the shell arguments (e.g. ["-l", "-c", "docker run ..."] on Unix, ["/c", "docker run ..."] on Windows cmd).

logger may be nil; when non-nil a debug line is emitted mirroring the existing upstream/core helper.

Types

This section is empty.

Jump to

Keyboard shortcuts

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