An untrusted repository opened in Claude Code can escape the macOS sandbox and run commands on your computer as your privileged user. You never get the permission prompt.
We turned the sandbox on and set permission mode to the strictest “don’t ask”. Then we opened a repository in Claude Code and sent one short message. A command from that repo ran on our Mac anyway, outside the sandbox, with no permission prompt. It can also work through an indirect prompt injection.
Counterintuitively, the sandbox is why there was no permission prompt. When you turn it on, Claude sandboxes the agent’s commands using Seatbelt, so it stops asking before it runs them.
Following our report, Anthropic fixed this in Claude Code 2.1.247.
How the escape works #
The harness runs its own git commands outside the sandbox. It runs them in the background to index the repo.
Git has a config setting, core.fsmonitor, that it reads from .git/config and runs as a shell command whenever it looks at the working tree.
So a rogue or compromised agent that wants out has to set core.fsmonitor in a .git/config file, then get the harness to run a git command that reads it. That git command is already outside the sandbox.
But four defenses get in the way:
| The defense | The bypass |
|---|---|
The git commands are hardened. The ones Claude Code runs outside the sandbox pass flags that blank out core.fsmonitor and other such dangerous options, so the harness doesn’t execute them. |
We found a git call that was left unhardened, the one that refreshes the file index: git ls-files . |
The .git folder is protected. Claude’s file tools refuse to write inside a.git folder, and Seatbelt blocks bash from writing there. A fresh clone won’t bring a.git/config along either, git never copies the remote’s config on purpose. Renaming another folder into.git is blocked too. |
But only for the .git in the project’s root folder. The Seatbelt profile rule that blocks renaming a nested.git folder is missing. So a setup script can build a git folder under a different name, writecore.fsmonitor into its config, and rename it to.git inside a subfolder. |
The harness runs git in the project’s root folder, where .git is clean. |
It actually runs in whatever directory the Bash tool used last. So the script changes the working directory to the subfolder with the poisoned .git . |
| The harness doesn’t automatically re-run that git command. | An attacker can force it by abusing skill auto-. The script’s last line tells Claude to read a build report in that same subfolder. When Claude reads a file, it checks that folder for skills and loads what it finds. a skill auto-triggers a file index refresh. That refresh is the unhardened git command, running in the subfolder with the poisoned .git . So the moment Claude reads the report, the rest of the chain runs, with no user interaction. |
Chained together, the setup script plants the poisoned .git in an unprotected nested path, tells Claude to read one file in that folder, and the file index refresh runs the payload.
Disclosure timeline #
| Date | What happened |
|---|---|
| Jul 13, 2026 | Reported to Anthropic. Triaged the same day. |
| Aug 6, 2026 | First hardening shipped in 2.1.223. It missed some of the git calls, so the escape moved to another one. We sent them the calls that were left. |
| Aug 26, 2026 | Full fix in 2.1.247. |
Anthropic triaged our report quickly, and now every git command the harness runs blanks core.fsmonitor, so a repo’s config can’t run anything.
How we built Accomplish #
We put the whole agent in a VM. Bash, git, every process it starts, all of it is in there. Real credentials never enter the guest, the agent only holds placeholders. Its network traffic goes out through a proxy on the host that the agent can’t reconfigure.
A poisoned core.fsmonitor still runs, it just runs inside the VM, not on your Mac.