Home/Blog/Sandbox Modes: Safe Pipeline Execution on Every Platform
securitysandboxinternals

Sandbox Modes: Safe Pipeline Execution on Every Platform

A look at how Plumber isolates pipeline steps on Windows, macOS, and Linux — from clean-env to Linux user namespaces and macOS Seatbelt — and why we chose each approach.

AO

Abi O.

Creator

18 May 20255 min read

Running arbitrary shell commands from a pipeline locally raises an obvious question: what prevents a buggy or malicious sh step from writing to /etc or reading your SSH keys?

Plumber has four sandbox modes. Each uses the strongest isolation available on the platform without requiring any extra installation.

Clean environment (all platforms)

The baseline for every platform: shell steps inherit a minimal, clean environment instead of your full shell session. Plumber passes through only the variables that tools actually need:

PATH, HOME, USER, LOGNAME, SHELL LANG, LC_ALL, LC_CTYPE TMPDIR, TEMP, TMP GOPATH, GOROOT NODE_PATH, NPM_CONFIG_CACHE JAVA_HOME, MAVEN_HOME, GRADLE_HOME SSH_AUTH_SOCK, SSH_AGENT_PID GIT_SSH, GIT_SSH_COMMAND

Your AWS_SECRET_ACCESS_KEY, personal git tokens, and other secrets are never passed through. Plumber also always injects CI=true and PLUMBER=1 so your scripts can detect the environment.

Linux: User namespaces

On Linux, Plumber uses unshare to run each step in an isolated namespace:

bashcode
unshare --user --pid --fork --map-root-user sh -c "your command"

What this provides:

  • User namespace — the process maps its UID to root inside the namespace but has no extra privileges on the host
  • PID namespace — every spawned process is automatically killed when the step finishes, leaving no orphan build processes

unshare is part of util-linux, installed on every Linux distro. Zero extra dependencies.

macOS: Seatbelt

macOS ships with sandbox-exec in every installation. Plumber wraps steps with a Seatbelt profile that uses a deny-on-system-paths / allow-everywhere-else approach:

  • All filesystem reads are allowed (tools need to read /usr/lib, /Applications, etc.)
  • Writes to /etc, /usr/bin, /System, /Applications are denied
  • Writes to the workspace and /tmp are explicitly allowed
  • Network access is unrestricted — git push/pull and package downloads work normally

Windows: WSL or Job Object

On Windows with WSL installed, steps run inside the Linux environment, giving you the full Linux namespace stack for free.

Without WSL, Plumber falls back to a Win32 Job Object — a kernel mechanism that groups the process and all its children together so they can be tracked and terminated as a unit. If a step times out or the pipeline is cancelled, every child process is killed cleanly.

WSL Python bootstrap

Debian and Ubuntu intentionally ship Python without ensurepip, which breaks python3 -m venv with a confusing error about the package not being available. Plumber detects this automatically at pipeline start and writes a minimal ensurepip shim into the user's local site-packages (~/.local/lib/pythonX.Y/site-packages/ensurepip/). The shim delegates pip installation to the system pip3 with no sudo or apt-get required. The check runs once per WSL session and is a no-op if ensurepip is already present.

How Plumber picks a mode

On startup, Plumber probes for the strongest available mode and selects it automatically:

PlatformProbeSelected mode
Linuxunshare --user --fork --map-root-user trueUser namespaces
Linux (restricted)probe failsClean environment
macOSwhich sandbox-execSeatbelt
Windowswsl --statusWSL (+ Python bootstrap)
Windows (no WSL)fallbackJob Object

You can see and override the detected mode in Settings → Sandbox. If you're on a locked-down corporate machine where unshare is blocked, Plumber gracefully falls back to clean-env.

None of these modes require Docker, VMs, or root access on the host.