Safe --dangerously-skip-permissions for Claude Code.
Ziplock wraps Claude Code in two OS-level safety layers so you can run autonomously without worrying about prompt injection, malware downloads, or accidental rm -rf /.
ziplock
├─ SOCKS5 + HTTP CONNECT proxy (localhost)
│ └─ DNS-over-HTTPS → Cloudflare 1.1.1.3 (blocks malware + adult content)
│
└─ sandbox_init() → claude --dangerously-skip-permissions
└─ writes restricted to CWD, /tmp, $HOME (excluding most of ~/Library)
└─ reads blocked for ~/Library, /Library, /System (with developer tool carve-outs)
└─ all network forced through localhost proxy
Layer 1 — macOS Seatbelt Sandbox: Applied via sandbox_init() FFI (not sandbox-exec). Claude can write to the project directory, /tmp, and $HOME (excluding ~/Library, with carve-outs below). The broad home write access is required for Claude Code's LSP plugins (rust-analyzer, typescript, swift) which write throughout $HOME at startup. Reads to ~/Library, /Library, and /System are blocked, with carve-outs for developer tooling. Paths passed via --allow-path are canonicalized before insertion into the profile, preventing symlink-based bypasses of the ~/Library deny rule. Mach IPC (mach-lookup) is restricted to an explicit allowlist of ~65 named services — eliminating ~70 irrelevant GUI, media, Bluetooth, Siri, and iCloud services from the reachable attack surface. Two services (pasteboard.1, lsd.modifydb) are intentionally denied to block clipboard exfiltration and LaunchServices database writes.
Developer tool carve-outs (read + write unless noted):
~/Library/Caches— build tool caches (Go, npm, pip, Homebrew, Xcode)~/Library/Keychains— Claude Code OAuth token storage~/Library/Developer— xcodebuild DerivedData, CoreSimulator, archives~/Library/org.swift.swiftpm— Swift Package Manager package cache~/Library/Preferences— read-only; app preference plists~/Library/Security— read-only; trust settings for codesign~/Librarydirectory entry — read + write on the directory itself (not contents); required for codesign ancestor-directory checks/Library/Developer,/Library/Keychains,/Library/Security, system frameworks — read-only~/Library/Group Containers/<1Password entry>— read-only; detected at startup; required foropCLI vault data access~/.op— read-only;opCLI config and account credentials
Layer 2 — DNS-Filtering Proxy: SOCKS5 + HTTP CONNECT proxies resolve all DNS via DNS-over-HTTPS (DoH) to Cloudflare 1.1.1.3, which blocks known malware and adult content domains. DoH encrypts queries end-to-end, preventing interception. The sandbox forces all traffic through localhost — no bypass possible. Direct connections to public IPs are also blocked.
cargo install --path .# Launch Claude Code in the sandbox
ziplock
# Pass arguments to Claude
ziplock -- -p "refactor the auth module"
# Allow writes to additional paths
ziplock --allow-path /tmp/build-output
# Skip DNS filtering (filesystem sandbox only)
ziplock --dangerous-allow-network
# Verbose mode — logs proxy connections, blocked domains
ziplock -v| Attack | Mitigation |
|---|---|
Write to system files (rm -rf /, /etc, /bin) |
Sandbox blocks writes outside CWD/tmp/$HOME |
Modify ~/Library app data (cookies, Mail, Messages) |
file-write* deny on ~/Library; DerivedData/Caches/Keychains are the only write carve-outs |
Persist malware in ~/Library/LaunchAgents |
Covered by ~/Library write deny |
| Read browser cookies, app secrets, Safari data | Sandbox blocks reads to most of ~/Library; only developer subtrees are accessible |
| Download malware | Cloudflare 1.1.1.3 blocks known malware domains |
| Connect to C2/phishing sites | DNS filter blocks categorized threats |
| Bypass DNS via direct IP | Proxy blocks all public IP connections |
| Bypass proxy entirely | Sandbox blocks all non-localhost network |
| Escape sandbox via child process | Sandbox inherited by all children, cannot be removed |
Ziplock is effective against untargeted, opportunistic attacks — the prompt-injection-downloads-malware class of threat. It provides meaningful friction against targeted data exfiltration via novel domains or raw IPs. It provides no protection against an adversary who uses allowed domains as exfil channels, who specifically targets in-scope credentials like ~/.ssh private keys, or who crafts a prompt injection that kills user processes via pkill/kill (signal permission is unrestricted for user-owned processes — required for the build→kill→install→open dev workflow).
The adversary is malicious content in Claude's context — a prompt injection in a file Claude reads, a hostile webpage it fetches, or a compromised tool response. The adversary controls what Claude does, not what runs on the machine before ziplock starts. The user is trusted; ziplock protects the user from Claude, not Claude from the user.
| Attack | Blocked by |
|---|---|
Overwrite system files (/etc, /bin, /usr) |
Seatbelt file-write* deny default |
| Corrupt other users' home dirs | Seatbelt write restricted to $HOME |
Modify ~/Library (cookies, app state, Mail, Messages) |
Explicit file-write* deny on ~/Library subpath |
Persist malware in ~/Library/LaunchAgents |
Covered by ~/Library write deny |
Escape CWD via --allow-path symlink |
Paths canonicalized before SBPL insertion |
| SBPL injection via crafted path argument | " and \ rejected; null bytes caught by CString |
| Attack | Blocked by |
|---|---|
| Download/execute malware from known C2 domain | Cloudflare 1.1.1.3 returns 0.0.0.0 |
| Connect to known phishing/malware IP via domain | DNS filter |
| Bypass DNS filter via raw public IP | Proxy rejects non-private literal IPs |
| Bypass proxy entirely (direct outbound TCP) | Sandbox restricts network to localhost:* |
| Intercept or spoof DNS queries | All DNS over DoH (TLS to family.cloudflare-dns.com) |
| DNS rebinding (domain → private IP after allow) | Resolved IP checked against is_private_ip() |
| Attack | Blocked by |
|---|---|
| Spawn unsandboxed child process | Sandbox inherited across exec |
| Remove sandbox from a child process | Seatbelt cannot be removed once applied |
| Signal arbitrary other processes | Not blocked — see accepted trade-offs |
| Mach IPC sandbox escape via privileged service (CVE-2018-4280 class) | mach-lookup restricted to an explicit ~65-service allowlist; window server, Bluetooth, Siri, iCloud, media, and phone services are unreachable |
| Exfiltrate data via clipboard | com.apple.pasteboard.1 is not in the mach allowlist; Claude cannot read or write the system clipboard |
| Hijack file type handlers via LaunchServices | com.apple.lsd.modifydb is not in the mach allowlist; Claude cannot register new app bundles or override file associations |
| LaunchServices app launch to arbitrary registered apps | Limited by DNS proxy blocking malicious domains; lsopen is allowed (required for open MyApp.app dev workflow and OAuth login) but browser runs in its own App Sandbox |
| Attack | Why not blocked |
|---|---|
Read ~/Library/Keychains (enumerate credential names) |
Deliberate carve-out — required for gh and other developer tools |
Write ~/Library/Keychains (create/modify keychain entries) |
Deliberate carve-out — Claude Code stores OAuth tokens in the login keychain |
Read ~/Library/Group Containers/<1Password> and ~/.op |
Deliberate carve-out — required for op CLI vault access; only the detected 1Password group container subdirectory is granted |
op CLI can reach com.1password.1passwordHelper and com.apple.security.agent via Mach IPC |
Required for op to authenticate to 1Password Desktop; security.agent enables Touch ID/password authorization dialogs |
Read/write ~/Library/Developer (Xcode DerivedData, CoreSimulator) |
Required for xcodebuild to compile and sign Swift/ObjC projects |
List ~/Library directory contents |
Deliberate carve-out — codesign checks read/write permission on every ancestor directory before signing; ~/Library must be accessible or xcodebuild signing fails. Reveals which app folders exist in ~/Library. |
| Signal any user-owned process (SIGKILL terminal, editor, etc.) | signal is unrestricted for processes owned by the current user — required for pkill <AppName> in the build→kill→install→open dev workflow. SBPL has no mechanism to restrict signals by target process name, signal type, or process tree. A prompt injection crafting a pkill or kill command could kill the user's shell, editor, or other running apps. Cannot affect other users or escalate to root. |
Read ~/.ssh private keys |
~/.ssh is under $HOME, which must be readable for Claude to work |
| Open browser or other registered app via LaunchServices | lsopen is required for Claude Code's OAuth login flow and open MyApp.app; a prompt injection could open a browser to an attacker-controlled URL, mitigated by the DNS proxy blocking malicious domains |
| Connect to Docker/Podman/OrbStack socket and issue daemon API calls | Unix domain sockets are broadly allowed (required for mDNS, 1Password, and other IPC). Blocking specific container runtime sockets is impractical as new runtimes add new socket paths. If you run Docker, Claude can call the Docker API. |
Read ~/.aws, ~/.config, .env, etc. |
Same — Claude needs project file access; no way to distinguish |
| Exfiltrate to an uncategorized domain | DNS filter is Cloudflare's categorization list, not a whitelist |
Exfiltrate via allowed domains (github.com, pastebin.com) |
Legitimate domains are unblocked by design |
Write anywhere in $HOME outside ~/Library |
Required for LSP plugins and build tools at startup |
| SPM and xcodebuild nested sandboxing bypassed | XBS_DISABLE_SANDBOXED_BUILDS=1 and SWIFTPM_SANDBOX=0 are set as environment variables, disabling nested sandbox-exec calls that would fail inside ziplock's SBPL profile. ziplock's sandbox still constrains all child processes. For Xcode-managed Package.swift manifest evaluation, add -IDEPackageSupportDisableManifestSandbox=YES to the xcodebuild command line (per-invocation override; does not affect Xcode sessions outside ziplock). |
App-hosted xcodebuild test targets (tests with a TEST_HOST) not supported |
When xcodebuild runs inside ziplock's sandbox, launchservicesd drops the entire environment dictionary the caller passes for the target app launch and reconstructs a fresh env from the user session (Apple security mitigation — sandboxed callers cannot inject env into spawned apps). The test host app launches with no XCTestSessionIdentifier, no XCTestConfigurationFilePath, and no DYLD_INSERT_LIBRARIES, so the XCTest session never pairs with testmanagerd and xcodebuild test hangs indefinitely after "Testing started". Symptom in log stream: testmanagerd creates an XCIDESession on a socket, waits to pair with a specific session ID, then tears down 30s later with "Unpaired test sessions". Tests with no TEST_HOST work fine because xctest is posix_spawned directly and inherits env normally. Workaround: run xcodebuild test for app-hosted targets outside ziplock, or restructure to use a library-style test bundle. |
~/Library/Keychains is read and write accessible. Reads allow Claude Code to retrieve OAuth tokens via the Security framework. Writes allow Claude Code to store OAuth tokens in the login keychain — without this, re-authentication after sandbox profile changes would be broken. This means Claude can create new keychain entries or modify existing ones. Users who need stricter credential isolation should not run tools requiring keychain auth within Claude's scope.
Reading a keychain item's value also requires SecItemCopyMatching, which communicates with com.apple.SecurityServer via Mach IPC. com.apple.SecurityServer is in the mach-lookup allowlist (required for Claude Code's own OAuth token access), so this call succeeds. Ziplock does not prevent Claude from reading keychain secret values.
- Uncategorized domains: A freshly registered exfiltration domain that Cloudflare hasn't categorized will resolve normally.
- Steganographic exfiltration: Data encoded in DNS query names (
data.attacker.com) passes through to Cloudflare; query content is not inspected. - Allowed domains as exfil channels: Claude can POST to
github.com,pastebin.com, etc. — all legitimate, all unblocked.
| Ziplock | Anthropic sandbox-runtime | Claude Code /sandbox |
Docker Sandboxes | cco | |
|---|---|---|---|---|---|
| Isolation mechanism | macOS Seatbelt (sandbox_init FFI) | Seatbelt (sandbox-exec) / bubblewrap | Seatbelt / bubblewrap | MicroVM (hypervisor) | sandbox-exec / bubblewrap / Docker |
| Sandbox applied by | ziplock before exec | Claude Code itself | Claude Code itself | Docker daemon | shell wrapper |
| Escape hatch | None | No | Yes — Claude can retry with dangerouslyDisableSandbox |
No | No |
| DNS malware filtering | Yes (Cloudflare 1.1.1.3 DoH) | No | No | No | No |
| Direct IP blocking | Yes (public IPs blocked) | No | No | Configurable | No |
| Network policy | Localhost-only + filtered proxy | Domain allowlist (user-confirmed) | Domain allowlist (user-confirmed) | Allow/deny lists | On/off |
| Single binary | Yes (Rust) | No (npm/Node.js) | Built-in | No (Docker daemon) | No (shell + deps) |
| macOS support | Yes | Yes | Yes | Yes | Yes |
| Linux support | No | Yes (bubblewrap) | Yes (bubblewrap) | Yes | Yes |
| VM-level isolation | No | No | No | Yes | No |
| Child process inheritance | Yes (kernel-enforced) | Yes | Yes | Yes | Yes |
The main tradeoffs: Docker Sandboxes offers stronger isolation (hypervisor boundary) but requires Docker Desktop and has startup latency. Anthropic's official /sandbox is built-in but has an escape hatch. Ziplock is the only option with DNS-level malware filtering.
- macOS (uses
sandbox_initAPI) - Claude Code installed and on
$PATH
MIT