All posts
Unproven Execution

GitSpawn: Pre-Trust Code Execution in AI Coding Agents

Manifold's GitSpawn discloses eight code-execution flaws letting a nested .git/config run shell in AI coding agents before the trust prompt fires

Securityv0 Intelligence Team OWASP: ASI05 sv0 finding: unproven_execution
claude-code ai-coding-agent gitspawn cve-2026-45033 unproven-execution asi05

The Incident

On September 1, 2026, Manifold Security published “GitSpawn,” a coordinated disclosure of eight silent, pre-authentication code-execution flaws across seven CLI-based AI coding agents: Claude Code, Codex, Cursor, Grok Build, GitHub Copilot CLI, Hermes Agent, Goose, and Qwen Code. Four of the eight findings remained unpatched at publication.

The mechanism is one Git primitive. Each affected agent auto-invokes git status or git diff at session startup to build repository context. Git’s automatic bare-repository discovery walks the working tree looking for anything that resembles a bare repo — a directory containing HEAD and config — and reads that config. An attacker embeds vendor/malicious.git/ (or any other innocuous-looking path) in an otherwise legitimate project, sets core.fsmonitor (a Git performance setting whose value is a shell command Git invokes to enumerate changed files), and Git executes that command outside the agent’s sandbox, as the developer’s shell user. No prompt injection. No model interaction. On Claude Code and Hermes Agent, the payload fires before the workspace-trust prompt is accepted. On Qwen Code (v0.22.3), before the user has authenticated. On Grok Build (v1.0.13), on the first keystroke.

GitHub Security Lab shipped the pattern’s first CVE: CVE-2026-45033 for @github/copilot versions ≤ 1.0.42 (CVSS 8.5 High, GHSA published 2026-05-06, credited to syvb), fixed in Copilot CLI 1.0.43. The fix injects safe.bareRepository=explicit into every Git subprocess via GIT_CONFIG_COUNT / GIT_CONFIG_KEY_* / GIT_CONFIG_VALUE_* environment variables, so Git refuses to auto-discover nested bare repos. Anthropic silently patched the core.fsmonitor path in Claude Code 2.1.196 (reported 2026-06-26, fixed 2026-06-29) with no public advisory; a second Claude Code path reachable through the claude ultrareview command uses a different Git configuration key and remained live on 2.1.252 against the then-current 2.1.258 as of the September 1 disclosure. Goose shipped a fix in 1.44.0. Hermes Agent, Qwen Code, and Grok Build were unpatched at publication.

MITRE ATT&CK coverage: T1195.002 Compromise Software Supply Chain, T1546 Event Triggered Execution, T1059 Command and Scripting Interpreter, T1204.002 User Execution: Malicious File.

The Authority Path That Failed

The identity carrying execution authority at the moment of failure is the AI coding agent’s process, running as the developer’s shell user — home directory, SSH keys, cloud-CLI tokens, browser profile, Git credential helper, everything that account can reach. The developer intended to grant that identity a narrow scope: “read this repository and let me approve before you touch anything,” gated by the workspace-trust prompt (or, on Qwen Code, by sign-in; on Grok Build, by the first keystroke). The exercised scope is arbitrary command execution as the developer, before any of those gates fire.

The trust anchor that failed first is Git’s own automatic bare-repository discovery. When Git walks the tree for git status, any directory containing HEAD and config is honored as a repo, and the config’s core.fsmonitor value runs as a shell command. No agent stripped, sandboxed, or overrode that config before spawning git. The held-vs-exercised gap — the developer’s full shell identity vs “compute a summary of changed files” — was never inspected because the mental model that presents the workspace-trust prompt as a code-execution gate was never true. The prompt gates the agent’s own reasoning-loop tool calls, not the subprocesses the agent already spawned to figure out where it is. This is the third public rediscovery of the same class in nine months: Sonar’s December 2025 Claude Code CLI pre-trust arbitrary code execution (patched 2.0.71); Novee’s February 2026 Cursor Git Hooks flaw CVE-2026-26268 (CVSS 9.9, patched in Cursor 2.5); and now Manifold’s ecosystem-wide sweep.

SecurityV0 Perspective

This is unproven_execution / ASI05. Every affected agent silently attached a code-execution capability — git, with core.fsmonitor as the shell-command lever — that the deploying operator never authorized. The developer installed an AI coding assistant on the promise “look at my code, ask before acting.” What each agent shipped instead is “look at my code by spawning Git against whatever config the working tree contains, before the trust prompt renders.” That is the same pattern SecurityV0 mapped in the Langflow CSV Agent’s silent Python REPL and the Cursor Git Hooks CVE-2026-26268 disclosure: a capability the operator did not knowingly attach becomes the agent’s most powerful tool, executed on first ingestion of attacker-controlled input.

The evidence pack SecurityV0 would produce inventories every AI coding agent installed on developer hosts, resolves each installation to a specific version against the vendor’s fixed release (Copilot CLI ≥ 1.0.43; Claude Code ≥ 2.1.196 for the core.fsmonitor path but still exposed through 2.1.258 for the second claude ultrareview path; Goose ≥ 1.44.0; Cursor 2.5 for the earlier CVE-2026-26268 hook-file variant), maps the file paths each agent would read from .git/, and captures whether the agent spawns Git before the workspace-trust prompt or sign-in gate. Pre-clone, the pack answers the operator question: which agent installations on which developer hosts would honor a nested vendor/malicious.git/config before any gate fires? Post-exploitation, it answers the forensic question: which agent runs in the past 90 days spawned a process whose parent chain includes git inside a directory the developer never git inited, and what command did that spawn?

What To Do

  • Pin developer hosts to the fixed versions and treat unpatched agents as untrusted. @github/copilot ≥ 1.0.43 closes CVE-2026-45033; Claude Code ≥ 2.1.196 closes the core.fsmonitor path but the claude ultrareview code path remained live on 2.1.258 as of publication; Goose ≥ 1.44.0. Enforce agent versions via MDM and gate repository access on a pre-clone version check — the IDE version is a fleet inventory question, not a developer preference. Hermes Agent, Qwen Code v0.22.3, and Grok Build v1.0.13 were unpatched at Manifold’s publication; treat those installations as blocking findings until the vendor ships a fix.
  • Export safe.bareRepository=explicit from every agent runtime, not just Copilot CLI. Copilot CLI’s fix works — Git refuses to auto-discover nested bare repos when this is set — but the fix is per-process env-var injection, and any agent that spawns Git without also setting it inherits the same class of flaw. Roll the setting into the Git configuration used by every AI coding agent runtime you run — developer workstations, CI runners, and any container image an agent runs inside. Verify each installed agent’s subprocess environment includes it before the agent runs on untrusted content.
  • Do not treat the workspace-trust prompt as a code-execution boundary. On four of the seven agents Manifold examined, code from a nested .git/config executes before that prompt is presented. Any downstream policy that reasons “the developer had to click through, so agent code has consent” is unsound for the entire class. Explicit policy: no AI coding agent runs against a freshly cloned repository until the repository has been scanned for nested .git directories, and the workspace-trust prompt is an additional gate, not the first one.
  • Instrument subprocess spawn from AI coding agent processes. For every developer host running an agent (Claude Code, Codex, Cursor, Grok Build, Copilot CLI, Hermes, Goose, Qwen Code, and their successors), log execve calls whose parent chain includes the agent’s binary, especially those spawning git. GitSpawn is not the last shape this class will take — core.hookspath, core.pager, core.editor, credential.helper, diff.external, and any config key whose value Git treats as a program name are all leverage. The signal to keep is “agent spawned Git inside a directory the developer did not create.”
  • Refuse the safe.bareRepository=explicit collateral breakage as an excuse. The fix pattern has caused legitimate-workflow breakage in SwiftPM caches, Flutter iOS builds, VS Code Git integration, and Copilot CLI itself because the setting is exported into the shared process environment and other tools rely on bare-repo operations. Scope the environment variable to the agent’s subprocess tree only, not the developer’s ambient shell. Do not follow the workaround of unsetting safe.bareRepository=explicit to unbreak SwiftPM — that path re-exposes the class.

Sources