2026-02-22
Claude Code's --dangerously-skip-permissions ("YOLO mode") bypasses all interactive permission prompts, enabling fully autonomous execution. While productive, it introduces risks: arbitrary code execution, data exfiltration, and destructive filesystem operations. The built-in sandbox exists but isn't automatically enabled with YOLO mode, and allowUnsandboxedCommands defaults to true, letting Claude retry blocked commands outside the sandbox without approval.
A shell function claude-safe-yolo (works in both bash and zsh) that launches Claude Code in YOLO mode with Anthropic's srt (sandbox-runtime) enforced via a settings overlay file, plus permission deny rules for destructive and external-facing operations.
| File | Purpose |
|---|---|
~/.claude/safe-yolo-settings.json |
Settings overlay with sandbox config + permission deny rules. Only loaded when claude-safe-yolo is invoked via --settings flag. Does not affect normal claude usage. |
~/.zshrc or ~/.bashrc |
claude-safe-yolo function added to the user's shell config. The function is compatible with both bash and zsh. |
~/.claude/sandbox-self-test.sh |
Automated self-test script. Launches a sandbox session that tests all boundaries and produces a report. |
{
"permissions": {
"deny": [
"Bash(git push:*)",
"Bash(git push)",
"Bash(ghe push:*)",
"Bash(ghe push)",
"Bash(git reset --hard:*)",
"Bash(git reset --hard)",
"Bash(git clean -f:*)",
"Bash(git clean -f)",
"Bash(git clean -fd:*)",
"Bash(git clean -fd)",
"Bash(git checkout .:*)",
"Bash(git checkout .)",
"Bash(sudo:*)",
"Bash(sudo )",
"Bash(su :*)",
"Bash(su )",
"Bash(open -a:*)",
"Bash(open -a )",
"Bash(osascript:*)",
"Bash(osascript )",
"Bash(launchctl:*)",
"Bash(launchctl )",
"Bash(npm publish:*)",
"Bash(npm publish)",
"Bash(pnpm publish:*)",
"Bash(pnpm publish)",
"Bash(gh pr merge:*)",
"Bash(gh pr merge)",
"Bash(gh release create:*)",
"Bash(gh release create)",
"Bash(ghe pr merge:*)",
"Bash(ghe pr merge)",
"Bash(ghe release create:*)",
"Bash(ghe release create)",
"Bash(gh issue close:*)",
"Bash(gh issue close)",
"Bash(gh repo delete:*)",
"Bash(gh repo delete)",
"Bash(gh pr close:*)",
"Bash(gh pr close)",
"Bash(ghe issue close:*)",
"Bash(ghe issue close)",
"Bash(ghe repo delete:*)",
"Bash(ghe repo delete)",
"Bash(ghe pr close:*)",
"Bash(ghe pr close)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Read(~/.gnupg/**)",
"Read(~/.netrc)",
"Read(~/.npmrc)",
"Bash(cat ~/.ssh:*)",
"Bash(cat ~/.aws:*)",
"Bash(cat ~/.gnupg:*)",
"Bash(cat ~/.netrc:*)",
"Bash(cat ~/.npmrc:*)"
]
},
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"allowUnsandboxedCommands": false,
"network": {
"allowLocalBinding": true,
"allowUnixSockets": ["~/.1password/agent.sock"],
"allowedDomains": [
"api.anthropic.com",
"*.anthropic.com",
"sentry.io",
"*.sentry.io",
"github.com",
"*.github.com",
"registry.npmjs.org",
"pypi.org",
"packagist.org",
"*.googleapis.com",
"*.google.com",
"*.docker.io",
"formulae.brew.sh",
"objects.githubusercontent.com",
"*.a8c.com",
"*.wordpress.com",
"proxy.golang.org",
"sum.golang.org",
"storage.googleapis.com",
"files.pythonhosted.org"
]
}
}
}Key settings:
allowUnsandboxedCommands: false-- closes the escape hatch. The single most impactful change. Claude cannot retry blocked commands outside the sandbox.autoAllowBashIfSandboxed: true-- sandboxed bash runs without double-prompting in YOLO mode.enabled: true-- activates Seatbelt (kernel-level filesystem isolation) + HTTP/SOCKS proxy (domain-level network filtering).allowLocalBinding: true-- opens all localhost ports for bind/inbound/outbound, allowing local dev servers and services. This is the only srt option available -- there is no per-port allowlist.allowUnixSockets-- 1Password SSH agent socket allowlisted for local key access (though outbound SSH on port 22 is still blocked by the network proxy).- Permission deny rules -- block destructive git operations, privilege escalation, sandbox escape vectors, accidental publishing, external-facing gh/ghe actions, and reads of sensitive credential files.
- Read deny rules -- block Claude's
Readtool andcatvia Bash for~/.ssh,~/.aws,~/.gnupg,~/.netrc,~/.npmrc. Kernel-level sandbox also blocks reads of~/.sshindependently.
The --settings flag performs a deep merge with existing settings (~/.claude/settings.json), not a replace. The overlay adds to the base configuration:
| Base Setting | Behavior | Impact |
|---|---|---|
permissions.deny |
Appended (union) | Overlay's deny rules are added to the base's deny list |
permissions.allow |
Preserved from base | Irrelevant in YOLO mode (everything auto-allowed) |
hooks |
Both sources run | Notification, PreToolUse, SessionEnd, etc. hooks still fire |
enabledPlugins |
Preserved | All plugins load normally |
sandbox |
Added from overlay | Base has no sandbox config, so overlay applies cleanly |
model, alwaysThinkingEnabled, etc. |
Preserved | Unchanged |
Caveat: This merge behavior is empirically confirmed but officially underspecified in the docs.
# Add to ~/.zshrc (zsh) or ~/.bashrc (bash)
claude-safe-yolo() {
# Pre-flight: srt must be installed for sandbox to work
if ! command -v srt &>/dev/null; then
echo "Error: srt (sandbox-runtime) not installed."
echo "Install with: npm i -g @anthropic-ai/sandbox-runtime"
return 1
fi
# Pre-flight: warn if not in a git repo (no /rewind safety net)
if ! git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
echo "Warning: Not in a git repo. No /rewind safety net available."
fi
# Launch Claude with YOLO + sandbox overlay
claude --dangerously-skip-permissions \
--settings ~/.claude/safe-yolo-settings.json \
"$@"
}Behavior:
- Hard block if
srtis missing -- sandbox won't work without it. - Soft warning if not in git -- proceed anyway for legitimate non-repo work.
- All args pass through -- supports
-p,--model,--resume, etc.
~/.claude/sandbox-self-test.sh [directory]Launches a sandboxed claude -p session that runs 30+ tests across all sandbox boundaries and produces a markdown report saved to /tmp/sandbox-self-test-report.md. Tests cover filesystem writes/reads, network filtering, escape hatch, permission deny rules, legitimate operations, and MCP access. The script is bash-compatible and does not depend on the shell function — it inlines the same logic.
Tested with Claude Code 2.1.50 and srt 1.0.0.
Initial manual tests (Tests 1-10) established baseline behavior and uncovered issues that were iteratively fixed: token injection for Keychain bypass, git checkout . deny pattern fix, allowLocalBinding for ghe proxy, and read deny rules for sensitive paths.
Full automated run via sandbox-self-test.sh: 41 passed, 0 failed, 3 info (44 total tests).
| Target | Result |
|---|---|
| CWD | Allowed |
$TMPDIR (/private/tmp/claude-<UID>/) |
Allowed |
/tmp directly |
Blocked -- operation not permitted |
~/Desktop |
Blocked -- operation not permitted |
~/.bashrc |
Blocked -- operation not permitted |
.git/hooks/pre-commit |
Blocked -- denyWithinAllow |
~/.gitconfig |
Blocked -- operation not permitted |
| Target | Method | Result |
|---|---|---|
~/.ssh/id_rsa |
Read tool | Blocked -- permission deny rule |
~/.aws/credentials |
Read tool | Blocked -- permission deny rule |
~/.gnupg/pubring.kbx |
Read tool | Blocked -- permission deny rule |
~/.ssh/id_rsa |
cat (Bash) |
Blocked -- kernel-level Operation not permitted |
~/.ssh/id_rsa |
head (Bash) |
Blocked -- kernel-level Operation not permitted |
| Source file in CWD | Read tool | Allowed |
The kernel-level sandbox blocks reads of ~/.ssh independently of permission deny rules. Both cat and head (which has no deny rule) are blocked at the kernel level.
| Target | Result |
|---|---|
api.github.com |
Allowed (HTTP 200) |
httpbin.org |
Blocked (exit 56, connection reset by proxy) |
evil-exfil-test.example.com |
Blocked (exit 56) |
registry.npmjs.org |
Allowed (HTTP 200) |
pypi.org |
Allowed (HTTP 200) |
SSH git@github.com |
Blocked (DNS resolution failed -- non-HTTP traffic can't route through proxy) |
dangerouslyDisableSandbox=true with curl httpbin.org -- blocked (exit 56). Escape hatch confirmed closed.
All denied at Claude Code permission level before shell execution: git push, git reset --hard, git clean -f, git checkout ., sudo, open -a, osascript, npm publish, gh pr merge, gh issue close, gh repo delete, gh pr close.
git status, git log, ls, node --version, python3 --version, Read tool on CWD files, Write tool on CWD files -- all allowed.
Linear, Context7, WordPress.com all succeeded. MCP servers use stdio transport and bypass the sandbox network proxy entirely.
Homebrew's gh is CGO-enabled and uses macOS Security.framework for TLS certificate verification, which the Seatbelt sandbox blocks. All gh/ghe commands that make HTTPS requests fail with x509: OSStatus -26276. Workaround: use Context A8C MCP for GitHub operations (stdio transport bypasses sandbox).
| Category | Status | Notes |
|---|---|---|
| Filesystem writes | PASS | Restricted to CWD + $TMPDIR |
| Filesystem reads | PASS | ~/.ssh blocked at kernel level; other sensitive paths blocked by permission deny rules |
| Network filtering | PASS | Domain allowlist enforced, non-HTTP traffic blocked |
| Escape hatch | PASS | dangerouslyDisableSandbox blocked by policy |
| Mandatory deny paths | PASS | .git/hooks and dotfiles protected |
| Legitimate dev ops | PASS | git, build tools, file ops work |
| Permission deny rules | PASS | All 12 destructive commands blocked |
| Git over SSH | BLOCKED | Port 22 can't route through HTTP/SOCKS proxy |
| gh/ghe CLI | BROKEN | CGO Go uses Security.framework for TLS; sandbox blocks it (x509: OSStatus -26276). Use MCP instead. |
| MCP servers | PASS | stdio transport bypasses sandbox |
| Boundary | Mechanism |
|---|---|
| Filesystem writes | Seatbelt allow-only: CWD + $TMPDIR (/private/tmp/claude-<UID>/) + ~/.claude/debug. Everything else denied at kernel level. |
| Filesystem reads | Kernel-level deny for ~/.ssh confirmed. Permission deny rules add coverage for ~/.aws, ~/.gnupg, ~/.netrc, ~/.npmrc via Read tool and cat. |
| Network | Seatbelt blocks all direct connections except localhost (opened by allowLocalBinding: true). Outbound internet goes through proxy which enforces domain allowlist. Non-HTTP traffic (SSH port 22) blocked at DNS resolution level. |
| Sensitive files (writes) | Mandatory deny on .bashrc, .zshrc, .gitconfig, .git/hooks/, .git/config, .git/refs, .git/objects, .git/HEAD, .mcp.json, IDE dirs -- hardcoded in srt, not overridable. |
| Escape hatch | allowUnsandboxedCommands: false -- Claude cannot retry outside sandbox. |
| Child processes | Inherit all restrictions via fork()/exec(). |
| Destructive operations | Permission deny rules block git push, git reset --hard, git clean, git checkout ., sudo, open -a, osascript, npm/pnpm publish, gh/ghe merge/close/delete. |
| Credential reads | Permission deny rules block Read tool and cat for ~/.ssh, ~/.aws, ~/.gnupg, ~/.netrc, ~/.npmrc. Kernel sandbox independently blocks ~/.ssh reads. |
| Tool | Status | Notes |
|---|---|---|
git (in-project) |
Works | CWD reads/writes allowed |
git push |
Denied | Permission deny rules block all push operations |
git over SSH |
Blocked | Port 22 can't route through HTTP/SOCKS proxy |
gh / ghe CLI (all ops) |
Broken | CGO Go uses macOS Security.framework for TLS verification, which the Seatbelt sandbox blocks. Fails with x509: OSStatus -26276. |
gh / ghe via MCP |
Works | Context A8C MCP uses stdio transport, bypasses sandbox entirely |
npm / pnpm |
Works | registry.npmjs.org in allowlist, CWD writes for node_modules |
| Build tools | Works | Execute from /opt/homebrew/bin/, write to CWD |
| MCP servers (all) | Works | stdio transport bypasses sandbox network proxy entirely |
allowLocalBinding: trueopens all localhost ports -- useful for local dev servers and services, but means any service on localhost is reachable from within the sandbox. There is no per-port allowlist in srt.- LaunchServices escape --
open -ais denied by permission rules but the underlying Seatbelt profile still allowscom.apple.coreservices.launchservicesdMach IPC. The permission deny is a semantic check, not kernel-enforced. - Domain allowlist is coarse -- allowing
github.commeans Claude could exfiltrate data to any GitHub endpoint viacurl(thoughgit pushis denied by permission rules). - gh/ghe CLI broken -- Homebrew's
ghis CGO-enabled and uses macOS Security.framework for TLS certificate verification. The Seatbelt sandbox blocks Security.framework access, causingx509: OSStatus -26276on all HTTPS requests. Workaround: use Context A8C MCP for GitHub operations (stdio transport bypasses sandbox). - SSH blocked entirely -- the network proxy only handles HTTP/HTTPS. All SSH connections (port 22) are blocked at DNS resolution level.
- Permission deny rules are pattern-matched -- they check the start of the command string. Creative variations (e.g.,
env git push) could bypass them. - Sibling tool call cascade -- when one tool call in a parallel batch is denied (e.g., Read on
~/.ssh), all sibling calls in the same batch fail with "Sibling tool call errored". Requires sequential retry.
One-time setup:
npm i -g @anthropic-ai/sandbox-runtime
# Reload your shell config
source ~/.zshrc # zsh
source ~/.bashrc # bashRun the automated self-test to verify sandbox boundaries:
~/.claude/sandbox-self-test.sh # from current directory
~/.claude/sandbox-self-test.sh ~/Work/repo # from a specific repoReport saved to /tmp/sandbox-self-test-report.md.
Based on a research document that evaluated five proposals for constraining YOLO mode on macOS. This implementation combines Proposal 1 (Hardened YOLO via Seatbelt + Proxy) with elements of Proposal 3 (permission deny rules for known-bad patterns). Final rating: Safety 9/10, Autonomy 8/10.