Skip to content

Instantly share code, notes, and snippets.

@tburnam
Created February 27, 2026 14:23
Show Gist options
  • Select an option

  • Save tburnam/5c94ca4f5eb08c59e01b815e0e66b5b0 to your computer and use it in GitHub Desktop.

Select an option

Save tburnam/5c94ca4f5eb08c59e01b815e0e66b5b0 to your computer and use it in GitHub Desktop.
macOS sandbox for claude code
#!/bin/bash
# safe-claude — run Claude Code in a macOS sandbox that restricts file writes
# Usage: ./safe-claude [-w /path/to/workspace] [claude flags...]
# Examples:
# ./safe-claude # sandbox cwd
# ./safe-claude -p "fix the bug" # sandbox cwd, pass -p to claude
# ./safe-claude -w ~/projects/foo # sandbox ~/projects/foo
# ./safe-claude -w . -p "hello" # sandbox cwd, pass -p to claude
# ./safe-claude --dangerously-skip-permissions # YOLO mode, sandboxed
#
# Setup (pick one):
# ln -s "$(pwd)/safe-claude" /usr/local/bin/safe-claude # then: safe-claude -p "fix the bug"
# alias claude='/path/to/safe-claude' # add to ~/.zshrc, then: claude -p "fix the bug"
WORKSPACE="."
# Parse flags (just -w/--workspace), pass everything else through
while [[ $# -gt 0 ]]; do
case "$1" in
-w|--workspace)
WORKSPACE="$2"
shift 2
;;
*)
break
;;
esac
done
WORKSPACE=$(cd "$WORKSPACE" && pwd)
cat > /tmp/claude-sandbox.sb << EOF
(version 1)
(deny default) ;deny all by default, but allow specific things:
(allow process*)
(allow signal)
(allow sysctl-read)
(allow mach*)
(allow ipc-posix-shm*) ;shared memory (cfprefs, notification center)
(allow system-socket) ;system sockets (PF_SYSTEM)
(allow file-read*)
(allow file-ioctl)
;; ── Workspace writes ──
(allow file-write*
(subpath "$WORKSPACE"))
;; ── Temp / OS plumbing ──
(allow file-write*
(subpath "/private/tmp")
(subpath "/private/var/folders")
(subpath "/var/folders")
(regex #"^/dev/ttys[0-9]+$")
(literal "/dev/null")
(literal "/dev/tty")
)
;; ── ~/.claude — fully writable (Claude's own operational space) ──
(allow file-write*
(subpath "$HOME/.claude"))
;; ── Other app data ──
(allow file-write*
(subpath "$HOME/.cache")
(subpath "$HOME/Library/Caches/claude-cli-nodejs")
(regex #"^$HOME/\\.claude\\.json")) ;.claude.json, .claude.json.lock, .claude.json.tmp.*
;; ── ~/.config — read-only (prevent poisoning other tool configs) ──
;; Claude reads from ~/.config but shouldn't need to write there.
;; If this breaks something, selectively allow specific subpaths.
;; ── Network — restrict to HTTPS + DNS + local MCP sockets ──
(allow network-outbound
(remote tcp "*:443") ;HTTPS
(remote udp "*:53") ;DNS
(remote tcp "localhost:*")) ;local MCP servers, LSPs, etc.
(allow network-inbound
(local tcp "localhost:*")) ;local server callbacks
(allow network* (local unix-socket)) ;unix domain sockets (MCP bridge, etc.)
EOF
sandbox-exec -f /tmp/claude-sandbox.sb claude "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment