sandbox-exec (macOS): command-line sandboxing
sandbox-exec is a built-in macOS command-line utility that runs a program inside a sandbox defined by a sandbox profile (a small Scheme/LISP-like policy file). The basic idea is to deny or allow specific operations (network, file reads/writes, process exec, etc.) so a command can only access what you explicitly permit.
Source: sandbox-exec: macOS's Little-Known Command-Line Sandboxing Tool | Igor's Techno Club (2025-04-17)
Quick start
Run a command under a profile file:
sandbox-exec -f profile.sb command_to_run
Sandbox profile basics
Profiles are built from parenthesized rules. Typical structure:
(version 1)- Default policy:
(deny default)or(allow default) - Specific allow/deny rules
Rules can target paths using:
- literal path:
(literal "/path/to/file") - regex:
(regex "^/System") - subpath (directory subtree):
(subpath "/Library")
Two common strategies
Deny-by-default (more secure)
Start from “nothing is allowed”, then open up only what’s required:
(version 1)
(deny default)
(allow file-read-data (regex "^/usr/lib"))
(allow process-exec (literal "/usr/bin/python3"))
This is ideal for untrusted code, but you’ll likely need iterations to make the program functional.
Allow-by-default (more convenient)
Start from “everything is allowed”, then block specific risky operations:
(version 1)
(allow default)
(deny network*)
(deny file-write* (regex "^/Users"))
This is easier to adopt, but it’s also easier to miss a capability you meant to deny.
Practical examples
Sandboxed terminal session (no network + limited personal file reads)
Create terminal-sandbox.sb:
(version 1)
(allow default)
(deny network*)
(deny file-read-data (regex "^/Users/[^/]+/(Documents|Pictures|Desktop)"))
Then start a sandboxed shell:
sandbox-exec -f terminal-sandbox.sb zsh
Use pre-built system profiles
macOS ships profiles under /System/Library/Sandbox/Profiles. You can use one directly:
sandbox-exec -f /System/Library/Sandbox/Profiles/weatherd.sb command
These can be a good starting point to copy/adapt.
Import and extend an existing profile
(version 1)
(import "/System/Library/Sandbox/Profiles/bsd.sb")
(deny network*)
Inline profile (good for quick “no network”)
alias sandbox-no-network='sandbox-exec -p "(version 1)(allow default)(deny network*)"'
Example:
sandbox-no-network curl -v https://google.com
Note: the source article reports this style didn’t reliably prevent network access for at least one GUI app invocation (Firefox).
Debugging: “why did my app fail in the sandbox?”
Console.app
Open Console (Applications → Utilities → Console), then search for “sandbox” and your app name. Look for messages containing “deny” to see what was blocked.
Live sandbox violation logs
Stream sandbox logs:
log stream --style compact --predicate 'sender=="Sandbox"'
Filter for an app (example from the source uses “python”):
log stream --style compact --predicate 'sender=="Sandbox" and eventMessage contains "python"'
Useful use cases
- Run untrusted scripts/tools: deny network and restrict reads outside a narrow allowlist.
- Privacy guardrails: explicitly deny reads of personal directories (Documents/Pictures/Desktop) for a subprocess.
- Safer automation: wrap a one-off data transform so it can’t exfiltrate or overwrite files outside a working directory.
- Developer testing: quickly test how an app behaves when permissions are constrained (before doing deeper App Sandbox work).
Limitations / caveats
- Apple discourages relying on
sandbox-execlong-term in favor of App Sandbox (per the source article’s “deprecation status” note). - Non-trivial apps can require lots of trial-and-error to permit the minimum needed capabilities.
- Major macOS updates may change behavior.
- No GUI for authoring profiles; you need to iterate using logs.