Skip to content

Claude Code

One git call let a repo escape the Claude Code sandbox

3 min read

Written by an AI model from the linked sources and checked against them by a second model. How we work

All about: Claude Code versions and changes →
Image: accomplish.ai

Turn on the Claude Code sandbox, set the permission mode to the strictest “don’t ask”, open a repository and send one short message - and a command from that repo runs on your Mac, outside the sandbox, with no approval prompt. Security firm Accomplish described the escape on September 16, and Anthropic fixed it in Claude Code 2.1.247.

The counterintuitive part is that the sandbox is why there was no prompt. Turn it on and Claude wraps the agent’s shell commands in Seatbelt, macOS’s built-in isolation layer, so the operating system enforces the limits while a command runs and Claude stops asking before each one. The protection that removes the questions is the same one the repo rode out through.

To index a repository, the harness - the program around the model that runs its tools - runs its own git commands in the background, and those run outside Seatbelt. Git has a setting, core.fsmonitor, that it reads from .git/config and executes as a shell command whenever it inspects the working tree. The same core.fsmonitor behaviour has already let untrusted repositories run code in Claude Code before an approval prompt. Anthropic had hardened the git calls the harness runs outside the sandbox so they pass flags that blank out that option and others like it. Accomplish found only one call left unhardened: the file-index refresh, git ls-files.

Three other defenses stood in the way, and each had a gap. Claude’s file tools refuse to write inside a .git folder, Seatbelt blocks bash from writing there, and renaming another folder into .git is blocked too - but only for the .git in the project root. The rule that blocks renaming a nested .git is missing, so a setup script can build a git folder under another name, write a poisoned core.fsmonitor into its config, and rename it to .git inside a subfolder.

The harness runs git in whatever directory the Bash tool used last, not in the clean project root, so the script changes the working directory into that subfolder. And it does not re-run the index refresh on its own, so the script’s last line tells Claude to read a build report in the same subfolder. Reading a file makes Claude check that folder for skills and load what it finds, and loading a skill triggers a file-index refresh. That refresh is the unhardened git call, now running in the subfolder with the poisoned config.

The moment Claude reads the report, the rest of the chain runs with no user interaction. It also works through an indirect prompt injection, where the instructions arrive planted in content the agent reads rather than typed by the user.

Accomplish reported the bug on July 13, and Anthropic triaged it the same day. A first hardening shipped on August 6 in 2.1.223, missed some of the git calls, and the escape moved to another one; Accomplish sent over the calls that were left, and the full fix landed on August 26 in 2.1.247. Every git command the harness runs now blanks core.fsmonitor, so a repo’s config can no longer run anything.

Accomplish builds its own agent product around a different boundary: the whole agent - bash, git, every process it starts - runs in a VM, real credentials never enter the guest, and its network traffic goes through a proxy on the host that the agent can’t reconfigure. A poisoned core.fsmonitor still runs there. It just runs inside the VM, not on your Mac.

No comments yet. Be the first.

Join the conversation

Sign in to leave a comment. It appears after moderation.