DEV Community

Matteo Poli
Matteo Poli

Posted on Originally published at reado.watermelon-studio.it

Your agent ran git. So did the repo

On October 2, Manifold Security published GitSpawn, and it is the kind of bug that is obvious the second you see it and invisible until then.

Coding agents call git all the time. Not when you ask them to commit: in the background, to gather context. What changed, what branch you are on, what the diff looks like. And git reads its settings from the repository's own .git/config. Some of those settings name a program to run. The one Manifold used is core.fsmonitor, a hook git calls to ask "which files changed?" whenever it refreshes the index. So a repository can ship this:

[core]
    fsmonitor = <any command>
Enter fullscreen mode Exit fullscreen mode

and the command runs, in the researchers' words, "as you, with your privileges, on your machine". Because it is the agent's own code spawning git, not the model asking for a tool call, it runs "outside the sandbox, without an approval prompt."

Who was affected

Manifold reported it to eight products between June and July. At publication, this was the state of things:

Agent Status at publication
Claude Code (core.fsmonitor) patched in 2.1.196
Claude Code (ultrareview) unpatched, 2.1.252
Goose patched in 1.44.0 (CVE-2026-72718)
Cursor patched
OpenAI Codex patched
Hermes Agent unpatched, 0.21.0 (CVE-2026-71963)
Qwen Code unpatched, 0.22.3
Grok Build unpatched, 1.0.13

There is one important limit. A git clone never carries .git/config, so the repository has to reach you as files: a zip from a client, a folder on a shared drive, a USB stick, an archive attached to a bug report. That sounds niche until you count how often "can you take a look at this?" arrives as a zip.

The sandbox was never the boundary

The interesting part isn't the specific key. It's where the line got drawn.

Agent vendors spent the last year building careful boundaries around what the model can do: sandboxed shells, approval prompts, allow-lists of commands. All of that is aimed at the model's tool calls. GitSpawn goes around it entirely, because the dangerous call isn't the model's. It's the product's own plumbing, the background git status nobody thinks of as an action at all.

And core.fsmonitor isn't alone. Git has a handful of settings that name programs, and some of them run during ordinary read-only commands. Clean and smudge filters, for example, are configured in .git/config and switched on per file by .gitattributes, and git can run them during a plain status. Every one of these is a line in a config file that turns "read the repository" into "run the repository".

I checked my own IDE

I build Reado, an IDE for reading the code your agent writes. It is not an agent, so I read the GitSpawn post thinking it was about other people's products.

Then I remembered what Reado does the moment you open a folder: it shows the git status in the file tree, the diff in the gutter, blame on the current line. That's three background git calls, from the app's own code, before you have read a single line. Exactly the shape Manifold described.

So I wrote the test: a repository with core.fsmonitor set to a command that drops a marker file, opened the way Reado opens it. Stock git ran the command. Reado's git calls ran it too.

The fix is the one Manifold recommends for vendors, git -c core.fsmonitor=false …, applied in the one place every git call in Reado is built, with the test kept so it stays fixed. It ships in Reado 1.33.2. The filter and diff drivers are next: closing those needs a trust question the first time a folder is opened, not just a flag.

I'm not telling this to look good. It took me about ten minutes to find, and the only reason I looked was that someone else had published the pattern. Which is the point: this class of bug lives in the parts of a tool nobody reads, because they "just call git".

What to do this week

From the advisory, and from fixing it once:

  • Check .git/config before you open a folder you received as files. Manifold's advice is blunt: "Any setting that names a program can run it." fsmonitor, hooksPath, sshCommand, anything under filter. or diff. that names a command.
  • Open untrusted repositories in a throwaway container or VM, not in the same session that has your SSH keys and cloud credentials.
  • Update your agent, and if you use one of the unpatched ones, don't point it at code you didn't clone yourself.
  • If you build tools that call git, do what we did: one place where git commands are built, the hardening flags in it, and a test with a malicious repository so it stays fixed.

The broader lesson for anyone working with agents: the risky surface is no longer just the code the agent writes. It is everything around it that we used to treat as inert: config files, hooks, attributes, the agent's own settings. Those deserve the same slow read as a build script, because that's what they are.

If you have found other "inert" files that turn out to run things, I'd like to hear about it in the comments or on Discord.

Originally published on reado.watermelon-studio.it.

Top comments (2)

Collapse
 
bloqarl profile image
Carlos (Bloqarl) •

This one hit close. Someone pointed out on my last post that git worktrees share hooks and config with the main repo, and I'm moving .git/config and hooks out of what the agent can write. GitSpawn is the scarier direction though: the config is hostile before the agent even starts.

Makes me wonder how many people open a client zip straight in their editor with an agent running. Is there a git flag that ignores the repo's .git/config completely, or do you have to turn off each setting one by one like core.fsmonitor?

Collapse
 
urion profile image
Matteo Poli •

Not that I know of. Git always reads the repository's own .git/config. You can skip the system and global files (GIT_CONFIG_NOSYSTEM=1, GIT_CONFIG_GLOBAL=/dev/null) but not the local one, so it's one setting at a time: -c core.fsmonitor=false, -c core.hooksPath=/dev/null, and so on. Filters and diff drivers are the awkward part, because .gitattributes switches them on per file.

For a client zip I'd do the boring thing: delete the .git folder before opening it, or open it in a container with nothing worth stealing, and only then let an agent near it.