git status ran their code
On September 2, 2026, Manifold Security disclosed GitSpawn: eight flaws across seven command-line coding agents where a repository's own .git/config runs a command on the developer's machine. The agent runs git status to see what branch it is on, git refreshes its index, and the repository's chosen command executes with the logged-in user's privileges, outside the sandbox, ahead of the trust prompt, and on one tool ahead of the login. No prompt, no tool call, no model turn. Bromure Agentic Coding does not patch that path. It moves the machine the path leads to.
You typed nothing and approved nothing. The agent had not finished starting, and on one of these tools you had not even logged in. A folder somebody sent you ran a command as you, and git is what ran it.
A contractor finishes a piece of work and sends you a zip of the project
directory in a shared folder. You unzip it, cd in, and start your coding
agent to get oriented. The agent begins by asking git what branch this is and
what is dirty. That is the whole exploit.
On September 2, 2026, The Hacker News reported a disclosure by Manifold Security covering eight flaws across seven command-line coding agents, published under the name GitSpawn. Between them the affected tools carry close to half a million GitHub stars, and Claude Code alone is past 77 million npm downloads a month. Four of the eight were still executing repository-supplied commands when Manifold retested them on September 1.
A performance setting whose value is a command
core.fsmonitor is a git option for people with enormous repositories.
Rather than walk the whole tree to find out what changed, git can call an
external helper program and ask it. The value of the setting is the command.
Git reads it from the repository's own .git/config and runs it whenever it
refreshes its index, which means on git status, on git diff, on the
plumbing every tool leans on.
Nearly every agent Manifold examined gathers project context by running those
commands at startup, and none of them looked at .git/config first. So the
repository names the command and git runs it. In the researchers' words, it
executes "with the full privileges of the logged-in user, outside the
sandbox." OpenAI, in its own advisory for the Codex variant, said the helper
"runs outside Codex's command sandbox and without a user-approval prompt."
A git clone does not carry any of this. Clone transfers objects and refs,
and the source repository's local config stays home. So the poisoned project
has to reach you as files, with its .git directory intact: a zip
archive, a shared drive, a synced folder, a USB stick. Manifold's list
describes how consultants, clients, and colleagues hand off work, and it is
the one delivery path with no pull request in it. Git's own guard against
hostile repositories, safe.directory, checks whether somebody else owns the
repository. You unzipped it, so you own it, and the guard has nothing to say.
The timing puts each agent's own protection one step too late. On Claude Code and Hermes Agent the payload fires before the workspace-trust prompt, the dialog that exists to ask whether you trust this directory. On Qwen Code it fires before the user has authenticated, and on Grok Build it fires on the first keystroke.
Eight findings, seven agents, four of them still open
Half of these are fixed. goose 1.44.0 carries the fix for
CVE-2026-72718, rated CVSS 7.0.
OpenAI shipped Codex CLI 0.131.0 and the 26.519.x desktop builds for
CVE-2026-19592. Cursor patched its variant. Manifold confirmed Claude Code's
core.fsmonitor path in 2.1.193 and Anthropic closed it in 2.1.196, without
publishing an advisory: Manifold notes that neither Claude Code finding
appears in npm's advisory records.
The other half has not moved. Hermes Agent has a CVE (CVE-2026-71963) and a
private advisory still untriaged after six contact attempts. The Qwen Code
maintainers accepted the report on July 7 and 0.22.3 still runs the command. Grok Build still runs it. And Claude Code has a second path,
through its ultrareview command, which abuses a different git configuration
key and was still live at 2.1.252. Manifold withheld that key's name rather
than hand out a reusable attacker template.
Anthropic patched the first path in three days. The class walked around it through a different key, in the same file, in the same product.
This is git's execution surface, inherited by every tool that shells out to
git, and git has more than one setting whose value is a command it will run
for you. Pagers, editors, SSH commands, hook paths, diff and filter drivers,
and aliases beginning with a shell escape all take command values. Every
vendor now has to work out, key by key, which of those a repository can reach
from its startup path, against a configuration schema that gains keys over
time. Manifold's recommended fix, sanitizing the git configuration during
background context operations and disabling core.fsmonitor, is a denylist
maintained against a namespace. The researchers found the pattern in more
agents than the report names.
The controls that would have caught this run downstream of it
Line up what a careful team has on a coding agent today: permission prompts on tool use, workspace trust, hooks that inspect a command before it runs, the transcript endpoints that ship a session to a compliance backend, and OpenTelemetry for permission decisions and bypasses.
All of them anchor to the agent's own loop, meaning a tool call the agent
decided to make or a turn the model took. GitSpawn produces neither. The
process that executes the payload is git, invoked by the agent for its own
housekeeping, and the command comes from a file the agent never read. There is
no tool call to approve and no hook to fire. A team watching agent transcripts
land in its SIEM sees a session that starts normally and shows nothing, the
same blind spot we wrote about
when Anthropic shipped its Compliance API:
a log anchored to what reached the model cannot see what ran before the model
was consulted.
The command is going to run. The question worth answering is what it finds.
Run it and see what it finds
Bromure Agentic Coding gives each workspace its own Ubuntu VM under Apple's hypervisor. The agent, its git, its package manager, and anything those three start all live in the guest. Drop the contractor's zip into a workspace, let the helper fire, and grant the attacker everything: pre-trust, pre-auth, root in the guest if you like.
Read the SSH keys. There are none to read. A workspace's private keys stay
on your Mac; the VM gets an SSH_AUTH_SOCK bridged over vsock to a host-side
agent. That protocol has a request meaning "sign this challenge" and no
request meaning "hand me the key."
Sweep the environment for credentials. They are all there, shaped the way
validators expect, and all of them are decoys. ANTHROPIC_API_KEY holds an
sk-ant-api03-brm-… fake, and so do ~/.git-credentials, ~/.aws/config,
~/.kube/config, and ~/.docker/config.json. Your real values live encrypted
on your Mac, and the proxy substitutes them onto the wire after the request has
left the VM, only when it is bound for the host that credential was minted for.
No switch turns this off. It is how credentials work in the app.
Pull down the second stage. The helper is a loader, a few hundred bytes
whose job is to fetch the real payload. That fetch runs into the workspace's
egress firewall. Set Unmatched traffic to Deny and the VM reaches the
hosts you listed and nothing else, enforced in two places: the virtual switch
matches by destination IP and DNS-snooped hostname across all protocols, and
the proxy matches again by TLS server name and, for web rules, by HTTP
method. A loader that reaches for a package registry instead meets the
supply-chain pipeline, where the age gate refuses versions younger than the
cutoff. A payload published yesterday cannot clear that bar.
Exfiltrate what it did find. Now something has to cross a wire your Mac owns. The proxy scans each outbound request against the workspace's minted decoys, headers and body, with an Aho-Corasick automaton. A decoy addressed to a host outside the scope it was minted for is the signature of a machine sending out something it should not know. The proxy blocks with HTTP 451 before a byte reaches the destination, pauses the VM, and raises an alert naming the credential and both hosts. Bromure then marks the workspace compromised, and it will not boot again until you wipe its disk and home.
Leave persistence. A modified .bashrc in the guest lasts as long as you
let the VM last. Erase home resets /home/ubuntu, Reset disk
re-clones the system disk from the signed base image, and
bromure-cli vm run --rm gives you a workspace that deletes itself when the
VM stops.
The interception has no environment variable to unset
A proxy the guest opts into is a proxy the guest can decline. Bromure's
virtual switch diverts the VM's port-80 and port-443 flows into the host
proxy itself: no HTTPS_PROXY to unset, nothing in the guest that turns it
off, and plain HTTP inspected the way HTTPS is. The process making the
request here never read the agent's configuration and is not the agent. It
is a helper binary git launched, and it rides the same wire as everything
else.
The row exists even though no tool call does
The host proxy writes a metadata record for each request out of the VM,
whichever process made it: time, host, port, method, path, status, latency,
and bytes measured before the credential swap. Firewall verdicts,
supply-chain decisions, and credential brokering land in the Security
Timeline (Window → Security Timeline…), and on an enrolled Mac they go to
the organization's event stream as egress.firewall, supply_chain.fetch,
and credential.exfiltration. An agent-side transcript of this attack is
blank while the wire's carries the request.
The guest can reach one thing on your Mac. A shared folder is a live,
read-write window into your real filesystem, and the compromise wipe leaves it
alone by design. Share the project directory rather than your home directory,
and the helper's find for *.pem walks a Linux home full of checkouts and
package caches instead of your keychain-backed logins.
The boundary that is not a step in the sequence
Read the four GitSpawn timing details together, ahead of the trust prompt,
ahead of authentication, ahead of the first keystroke, ahead of any model turn,
and they say one thing. Each protection is a step in a startup sequence, and
each new feature adds a step ahead of it. Cursor learned that in 2025 with a
repository-supplied mcp.json, and again this July with a worktree setup
command that ran before its own trust
prompt. Now it is git's turn, in seven
products at once, through a config key that has been in git for years.
A hypervisor is not a step in that sequence. It does not read .git/config.
No refactor reorders it and no code path exempts itself from it, because it
does not run in the same program as the prompt. The VM was running before the
zip finished downloading, and its two guarantees, no real secrets in here and
no unmediated route out, hold the same at 2.1.193 and at 2.1.196.
Check your .git/config before you open a folder somebody sent you. Manifold
is right to say so. You will also do it for two weeks and then skip it on the
third zip of a busy afternoon, which is the shelf life of most instructions
that end "before you open it."
Install Bromure Agentic Coding, give the unfamiliar folder
its own machine, and let the config run whatever it likes in a room where
running is all it gets to do.