GitSpawn: How a Repo's .git Config Makes Claude Code, Codex, and Cursor Run Attacker Code

·13 min read·Evergreen Tools Team
Terminal showing git configuration hijack risk for AI coding agents

💡 Tool TipHardening agent workstations? Scan configs with Evergreen Tools' Regex Tester and Diff Checker, and fingerprint files with the Hash Generator. Regex Tester, Diff Checker, Hash Generator

What if opening a project could run arbitrary code on your machine -- no button click, no model call, no workspace-trust prompt? That is the GitSpawn vulnerability class Manifold Security disclosed in early September 2026: eight flaws across seven command-line AI coding agents in which a repository's own Git configuration names a command that the agent runs on the developer's machine, outside the sandbox and without an approval prompt. OpenAI published three Codex CVEs for the identical class the same day. As of the September 1 retest, fixes had shipped for goose, Claude Code, and Cursor, while Hermes Agent, Qwen Code, Grok Build, and a second Claude Code path were still executing repository-supplied commands.

1. Anatomy: core.fsmonitor and Config Keys Whose Value Is a Command

Git has a family of config keys whose value is literally a command. The best known is core.fsmonitor: Git runs whatever it names whenever the index refreshes -- including every git status and git diff. It exists as a performance feature (let an external daemon tell Git which files changed), but any operation that reads it from the repository's own .git/config will execute its value. A malicious repo simply ships a .git/config with [core] fsmonitor = "python3 /tmp/payload.py". The one requirement is that the repo arrives as files with its .git directory intact: a shared archive, a shared drive, a sync folder, or a USB stick preserves it, whereas an ordinary clone does not.

// The attack in one picture. Git config supports keys whose
// VALUE IS A COMMAND. core.fsmonitor is the best known: Git
// runs it whenever the index refreshes -- including on
// 'git status' and 'git diff'. A repo ships this file:
// .git/config
[core]
    fsmonitor = "python3 /tmp/payload.py"
// Open the repo in an agent (or IDE). Agent runs 'git status'
// in the background. Git runs fsmonitor. Payload executes as
// the user, outside the sandbox, no approval prompt.

2. Why Agents Are Uniquely Exposed

IDEs and coding agents call git status and git diff in the background at session startup to figure out which branch they are on and which files have changed -- leaving the repository's configuration untouched, which is exactly the problem, because the read itself triggers fsmonitor. As Manifold puts it: 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. On Claude Code and Hermes Agent the payload fires before the workspace-trust prompt is accepted; on Qwen Code, before the user has authenticated; on Grok Build, on the first keystroke.

Security researcher inspecting a malicious repository archive
// Manifold Security (GitSpawn, disclosed September 2026)
// found this pattern in 7 CLI agents: goose, Claude Code,
// Codex, Cursor, Qwen Code, Hermes Agent, Grok Build.
// In goose, 'goose review' builds git invocations with one
// config flag and strips nothing else:
goose review   # runs git with -c core.quotePath=off
// GitHub advisory CVE-2026-72718 (CVSS 7.0): "running goose
// review inside a malicious repo runs attacker code -- no
// submitted prompt, no model call, no tool approval, no
// trust prompt. The command executes before goose ever
// contacts the model." Fixes shipped for goose, Claude Code,
// and Cursor; Qwen Code, Grok Build, Hermes Agent and a
// second Claude Code path were still exposed on Sep 1.

3. The Affected Agents and the CVE Landscape

Manifold's disclosure covers fixed releases for goose, Claude Code, and Cursor, plus unpatched status for Hermes Agent, Qwen Code, and Grok Build, with a second Claude Code path reached through claude ultrareview confirmed live on 2.1.258, the current release. GitHub assigned goose's issue CVE-2026-72718 (CVSS 4.0 base score 7.0) with a blunt advisory: running goose review inside a malicious repo runs attacker code -- no prompt, no model call, no tool approval, no trust prompt, and the command executes before goose ever contacts the model. OpenAI published three Codex CVEs including CVE-2026-19592, warning that the helper runs outside Codex's command sandbox and can read, change, or delete the user's files. Hermes Agent drew CVE-2026-71963. Sonar had reported the same sink in April; Anthropic moved git status behind the trust dialog in 2.0.34, yet the startup behavior returned in 2.1.193.

# Detection: list every local repo config that points a
# config key at an executable, and print its origin so you
# can tell repo-supplied values from user/global ones.
for d in $(find . -name .git -type d); do
  git --git-dir="$d" config --local --list --show-origin     | grep -E 'fsmonitor|hooksPath|core.editor|alias.|pager.|diff..*command'     && echo "SUSPECT: $d"
done
# Also audit global + system level:
git config --global --list --show-origin

4. This Is Not a Prompt-Injection Problem

Blaming this class on models being prompt-injected would be wrong: there is no prompt, and the model is often never contacted. The flaw is that agents treat local subprocesses as trusted plumbing while repository content, including metadata, is untrusted input. Sonar connects the dots to IDE history: the same trust-dialog bypass existed in Visual Studio Code before 1.63.1 (CVE-2021-43891) and JetBrains IDEs before 2021.3.1 (CVE-2022-24346). The lesson: any design that runs local commands when a project opens must treat repository metadata as untrusted input.

Shield icon representing sandbox and trust boundaries

5. Detection and Mitigation

Mitigation has four layers. First, update and pin agent versions -- Codex CLI is at 0.152.1 as of September 2, so installs pinned below 0.131.0 remain exposed. Second, change how repos arrive: reject archives containing .git and re-clone from a trusted remote. Third, after any trusted clone, pin the dangerous keys with git config core.fsmonitor false and git config core.hooksPath .git/hooks. Fourth, scan every local repository's config for command-valued keys such as fsmonitor, hooksPath, alias, and pager entries, printing their origin so repo-supplied values are obvious. The code blocks in this post include a working detection loop and an archive-blocking hook.

#!/bin/bash
# Safe handling: never let a repo arrive with its .git intact.
# Ordinary clones are safe (the server's config is not cloned);
# archives, shared drives, sync folders and USB sticks preserve
# .git -- that is the delivery vector. Reject or re-clone.
for f in "$@"; do
  if unzip -l "$f" 2>/dev/null | grep -q '/[.]git/'; then
    echo "BLOCKED: $f contains .git -- re-create from a clone"
  fi
done
# After any trusted clone, pin the dangerous keys:
git config core.fsmonitor false
git config core.hooksPath .git/hooks

6. The Deeper Lesson: Trust Boundaries

No exploitation in the wild had been reported as of September 2, and the CISA KEV catalog (1,687 entries) lists none of these CVEs -- but that is no reason to relax. GitSpawn draws a clean trust boundary: a repository fetched from the network is input and should never carry executable intent; treating it as a trusted environment is the vulnerability. The cheapest move for your team is done in five minutes: update agents, ban archives with .git inside, and audit config after every clone. It neutralizes an entire attack class.

// Patch hygiene: the vulnerable class ships in agent release
// channels. Pin and verify versions like any dependency.
const AGENT_FIXES = {
  goose: "fixed (CVE-2026-72718, CVSS 4.0 base 7.0)",
  claudeCode: "core.fsmonitor fixed in 2.1.196; ultrareview path open on 2.1.258 (Sep 1)",
  cursor: "fixed (reported 3 weeks earlier by Manifold)",
  codex: "fixed; installs pinned below 0.131.0 exposed (current 0.152.1)",
  qwenCode: "unpatched at 0.22.3 as of Sep 2",
  grokBuild: "unpatched (xAI closed earlier report as informative)",
  hermesAgent: "unpatched (CVE-2026-71963)",
};

📌 Frequently Asked Questions

What is GitSpawn?

GitSpawn is Manifold Security's name for a vulnerability class where a repository's own Git configuration (for example core.fsmonitor) names a command that a coding agent executes on the developer's machine outside the sandbox when it runs git status or git diff at startup.

What is GitSpawn?

GitSpawn is Manifold Security's name for a vulnerability class where a repository's own Git configuration (for example core.fsmonitor) names a command that a coding agent executes on the developer's machine outside the sandbox when it runs git status or git diff at startup.

What is GitSpawn?

GitSpawn is Manifold Security's name for a vulnerability class where a repository's own Git configuration (for example core.fsmonitor) names a command that a coding agent executes on the developer's machine outside the sandbox when it runs git status or git diff at startup.

What is GitSpawn?

GitSpawn is Manifold Security's name for a vulnerability class where a repository's own Git configuration (for example core.fsmonitor) names a command that a coding agent executes on the developer's machine outside the sandbox when it runs git status or git diff at startup.

What is GitSpawn?

GitSpawn is Manifold Security's name for a vulnerability class where a repository's own Git configuration (for example core.fsmonitor) names a command that a coding agent executes on the developer's machine outside the sandbox when it runs git status or git diff at startup.

Which agents are affected?

The disclosure covers seven CLI agents: goose, Claude Code, Codex, Cursor, Qwen Code, Hermes Agent, and Grok Build. Fixes shipped for goose, Claude Code, and Cursor; on September 1 Hermes Agent, Qwen Code, Grok Build, and Claude Code's ultrareview path were still exposed.

Which agents are affected?

The disclosure covers seven CLI agents: goose, Claude Code, Codex, Cursor, Qwen Code, Hermes Agent, and Grok Build. Fixes shipped for goose, Claude Code, and Cursor; on September 1 Hermes Agent, Qwen Code, Grok Build, and Claude Code's ultrareview path were still exposed.

Which agents are affected?

The disclosure covers seven CLI agents: goose, Claude Code, Codex, Cursor, Qwen Code, Hermes Agent, and Grok Build. Fixes shipped for goose, Claude Code, and Cursor; on September 1 Hermes Agent, Qwen Code, Grok Build, and Claude Code's ultrareview path were still exposed.

Which agents are affected?

The disclosure covers seven CLI agents: goose, Claude Code, Codex, Cursor, Qwen Code, Hermes Agent, and Grok Build. Fixes shipped for goose, Claude Code, and Cursor; on September 1 Hermes Agent, Qwen Code, Grok Build, and Claude Code's ultrareview path were still exposed.

Which agents are affected?

The disclosure covers seven CLI agents: goose, Claude Code, Codex, Cursor, Qwen Code, Hermes Agent, and Grok Build. Fixes shipped for goose, Claude Code, and Cursor; on September 1 Hermes Agent, Qwen Code, Grok Build, and Claude Code's ultrareview path were still exposed.

Why is a normal git clone safe but an archive is not?

git clone only fetches repository objects and never copies the server's .git/config. Archives, shared drives, sync folders, and USB sticks preserve the full .git directory, which is where the attacker plants the malicious configuration.

Why is a normal git clone safe but an archive is not?

git clone only fetches repository objects and never copies the server's .git/config. Archives, shared drives, sync folders, and USB sticks preserve the full .git directory, which is where the attacker plants the malicious configuration.

Why is a normal git clone safe but an archive is not?

git clone only fetches repository objects and never copies the server's .git/config. Archives, shared drives, sync folders, and USB sticks preserve the full .git directory, which is where the attacker plants the malicious configuration.

Why is a normal git clone safe but an archive is not?

git clone only fetches repository objects and never copies the server's .git/config. Archives, shared drives, sync folders, and USB sticks preserve the full .git directory, which is where the attacker plants the malicious configuration.

Why is a normal git clone safe but an archive is not?

git clone only fetches repository objects and never copies the server's .git/config. Archives, shared drives, sync folders, and USB sticks preserve the full .git directory, which is where the attacker plants the malicious configuration.

Is this prompt injection?

No. The payload fires without any model call or prompt: the agent runs git status in the background before it ever contacts the model, and Git executes the fsmonitor command as part of that read.

Is this prompt injection?

No. The payload fires without any model call or prompt: the agent runs git status in the background before it ever contacts the model, and Git executes the fsmonitor command as part of that read.

Is this prompt injection?

No. The payload fires without any model call or prompt: the agent runs git status in the background before it ever contacts the model, and Git executes the fsmonitor command as part of that read.

Is this prompt injection?

No. The payload fires without any model call or prompt: the agent runs git status in the background before it ever contacts the model, and Git executes the fsmonitor command as part of that read.

Is this prompt injection?

No. The payload fires without any model call or prompt: the agent runs git status in the background before it ever contacts the model, and Git executes the fsmonitor command as part of that read.

What should I do right now?

Update and pin agent versions (Codex CLI below 0.131.0 remains exposed); stop accepting archives that contain .git; after cloning run git config core.fsmonitor false; and scan local repositories for command-valued config keys.

What should I do right now?

Update and pin agent versions (Codex CLI below 0.131.0 remains exposed); stop accepting archives that contain .git; after cloning run git config core.fsmonitor false; and scan local repositories for command-valued config keys.

What should I do right now?

Update and pin agent versions (Codex CLI below 0.131.0 remains exposed); stop accepting archives that contain .git; after cloning run git config core.fsmonitor false; and scan local repositories for command-valued config keys.

What should I do right now?

Update and pin agent versions (Codex CLI below 0.131.0 remains exposed); stop accepting archives that contain .git; after cloning run git config core.fsmonitor false; and scan local repositories for command-valued config keys.

What should I do right now?

Update and pin agent versions (Codex CLI below 0.131.0 remains exposed); stop accepting archives that contain .git; after cloning run git config core.fsmonitor false; and scan local repositories for command-valued config keys.