TL;DR
- A repository you were sent as a zip can run commands on your machine the second you open it with Claude Code, Cursor, Codex, Goose, Qwen Code, Grok Build or Hermes. No click, no prompt, nothing on screen.
- The repo's own
.git/confignames the command. Agents rungit statusin the background to gather context, and git obeyscore.fsmonitorduring the index refresh. - Check any repo you did not clone yourself with
git config --get core.fsmonitorbefore you open it. Setting the global version of that flag does not save you, and I explain why below.
A contractor sends you a project as a zip. You unzip it, cd in, type claude. That is the entire attack.
Before the agent shows a workspace-trust prompt, before you type anything, on some agents before you have even authenticated, it runs git status in the background to work out where it is. Git refreshes its index to answer that. During the refresh it reads a setting out of the folder you just unzipped, and that setting names a program for git to run.
The program runs as you, with your privileges, outside the agent's sandbox. Manifold Security published this on 1 September 2026 and called it GitSpawn. Eight findings across seven agents. Four were still unpatched on the day they published.
The Vulnerable Config
The payload is two lines in a file almost nobody opens.
[core]
fsmonitor = <command>
That is .git/config inside the repository, not your global git config. core.fsmonitor is a real performance feature. On a huge repo, rather than stat-ing every file, git asks a helper program what changed. Documented, intended, shipped for years.
The agent never chose to run that program. It ran something ordinary to gather context:
git status --porcelain=2 --branch
git diff --name-only HEAD
Both refresh the index. The refresh calls fsmonitor. The command executes. Which git command the agent picked makes no difference, and core.fsmonitor is not the only setting of its kind, which is why one of Manifold's findings is not an fsmonitor bug at all.
Delivery is narrower than most of the coverage suggests, and being precise about it matters. git clone does not carry this. Neither does fetch or pull. Git never transfers a repository's config over the wire. The repo has to reach you as files with .git already inside: a zip, a shared drive, a sync folder, a USB stick. Manifold used a zip for every proof of concept.
Which is to say the vector is exactly how contractors hand off projects, how a colleague passes you a broken build, and how a client sends you their codebase.
Why Every Agent Got This Wrong
The flaw is not in the model and not in anything new. It is in the subprocess an agent spawns to figure out where it is.
Every CLI agent needs the same three things at startup: current branch, modified files, tracked paths. Everyone reaches for git, because git is right there and already knows. And everyone wrote the call the way you or I would write it, as a plain git status.
The gap is a mental-model one. The agent treats the folder as data it is about to read. Git treats the same folder as configuration it is about to obey. Workspace trust sits at the boundary between untrusted input and execution, but the context-gathering call fires before that boundary, from the agent's own process. The permission model never sees it.
Manifold's disclosure table is the part worth reading twice:
| Agent | Reported | Status at publication |
|---|---|---|
Claude Code (core.fsmonitor) |
26 June 2026 | Patched in 2.1.196 |
| Cursor | 8 July 2026 | Patched |
| OpenAI Codex | 20 July 2026 | Patched |
| Goose | 13 July 2026 | Patched in 1.44.0, CVE-2026-72718 |
| Qwen Code | 7 July 2026 | Unpatched, confirmed on 0.22.3 |
| Grok Build | 14 July 2026 | Unpatched, confirmed on 1.0.13 |
Claude Code (claude ultrareview) |
15 July 2026 | Unpatched, confirmed on 2.1.252 |
| Hermes Agent | 20 July 2026 | Unpatched, CVE-2026-71963 |
Five of the eight reports came back as duplicates of findings other researchers had filed independently. One was a duplicate of a report filed the same day. When several people trip over the same thing at once from different directions, it was never hiding.
Two details in that table deserve attention. Hermes went six contact attempts across five channels with no triage, and its CVE was assigned by VulnCheck rather than the vendor. And the second Claude Code finding turns on a different config key, which Manifold has deliberately left unnamed while it is unpatched. Patching fsmonitor is not the same as patching the class.
The Fix
Read .git/config before you point an agent at a folder you did not clone yourself. That is the whole defence, and it takes two seconds.
# the one that matters
git config --get core.fsmonitor
# the wider sweep, for any setting that names a program
git config --local --list | grep -Ei 'fsmonitor|hooksPath|sshCommand|pager|editor|filter\.'
Anything that comes back with a value is a program git will execute on your behalf. If you did not put it there, do not open that folder with an agent. Strip it first:
git config --local --unset core.fsmonitor
Now the part I want to be blunt about, because I have seen it repeated in several write-ups of this research as the mitigation:
git config --global core.fsmonitor false # this protects you from nothing
Git config precedence runs system, then global, then local, then worktree, then the command line. A repository's local .git/config wins over your global config every single time. A hostile repo simply sets its own value and your global false is overridden before it is ever consulted. It is a fine default for your own repos. It is not a shield.
If you ship an agent, the fix is one flag on every background call:
git -c core.fsmonitor=false status --porcelain=2 --branch
Strip the config on any git call the product makes that the user did not type.
FAQ
Q: Does cloning a malicious repository trigger GitSpawn?
A: No. Git never transfers .git/config over clone, fetch or pull. The repository has to arrive as files with its .git directory intact, which in practice means a zip, a shared drive, a sync folder or a USB stick.
Q: Does git config --global core.fsmonitor false protect me?
A: No. A repository's local .git/config overrides your global config, so a hostile repo just sets its own value. Inspecting .git/config before you open the folder is the check that actually works.
Q: Is Claude Code patched?
A: Partly. The core.fsmonitor path was fixed in 2.1.196. Manifold confirmed a second finding on the claude ultrareview path still unpatched on 2.1.252 as of 1 September 2026. Cursor, Codex and Goose are patched. Qwen Code, Grok Build and Hermes Agent were not at publication.
Q: Would a security scanner have caught this?
A: No, and I would not trust one that claimed it did. This is a configuration file in a directory you were handed, not a pattern in your source code. The git config check above is the defence.
I want to be straight about scope here. This is not something a code scanner catches. SafeWeave reads your source, your dependencies and your secrets, and it does not read .git/config. No SAST tool I know of does. For GitSpawn, the two commands above are the entire fix.
What a scanner is for is the other half of the same problem, which is that the code your agent writes after it starts up is also untrusted input you did not audit. I run SafeWeave as an MCP server in Cursor and Claude Code for that, and even a pre-commit hook with semgrep and gitleaks covers most of it. The principle holds either way. The agent's environment and the agent's output both need something checking them, and right now neither one checks itself.
Source: GitSpawn: A Single Flaw Lets Untrusted Repos Run Code in Claude Code, Codex, Cursor, and Grok, Francisco Rosales, Manifold Security, 1 September 2026.
Top comments (0)