Artificer Digital The Artificer's Grimoire

Scout: Confused-Environment Attacks: When the Harness Trusts Its Own Setup Material

agent-security harness-engineering coding-agents claude-code

In Brief

  • GitSpawn, the auto-mode break, and the instruction-privilege-escalation paper share a shape that is not prompt injection: in two of them the model follows no instruction at all, and in the third it follows attacker text only after the harness has re-labelled it as the user’s. Git honoured a config value, Python loaded a file from the current working directory, and a harness re-labelled tool output as a user request.
  • GitSpawn is a 2021-class IDE bug, fixed in VS Code in December 2021 and catalogued in a 2022 Git advisory, arriving in agents that run git status before any trust prompt, sandbox, or model call. As of Manifold’s September 1 retest, goose, Codex, Cursor, and Claude Code’s core.fsmonitor path were patched; Hermes Agent, Qwen Code, Grok Build, and a second Claude Code path through ultrareview were not, and that state may have moved since.
  • The Git controls that close the path are config overrides on every background call: per invocation with git -c core.fsmonitor=false, or for every call at once through GIT_CONFIG_* environment variables, the mechanism GitHub used for Copilot CLI. Copilot’s own value, safe.bareRepository=explicit, closes only the nested-bare-repository variant, not a repository’s own .git/config. A global core.fsmonitor false does not close it: one practitioner test found the repository’s local config overrides it, which is Git’s documented precedence.
  • Rehberger’s chain worked in three to four of five runs across three variants, small samples by his own account. Anthropic closed the report as Informative and called auto mode a best-effort classifier rather than a security guarantee. The classifier judges each command against intent, cannot see a consequence that emerges from Python’s module path, and in some runs denied the agent’s own cleanup command.
  • The paper reaches all 13 attack objectives on all six harnesses under unrestricted execution and on all three harnesses that offer automatic permission review. The reviewer allowed actions it had classified as risky because the reconstructed history presented attacker-derived text as user authorization. The authors propose no defense; the root cause they name is a harness that stops labelling content by its true provenance.

Why This Matters

The sandbox debate has been about what an agent may do once it decides to act. These three disclosures land underneath it. A repository’s .git/config ran a command before Claude Code showed its trust prompt. A zip planted a struct.py that Python’s base64 module imported on the agent’s next step. A harness handed a subagent an attacker’s text as the user’s own words. No jailbreak; where the model did follow attacker text, the harness had first relabelled it as the user’s. If your agents open folders they did not clone, extract archives, or delegate to subagents, this class is yours, and the patches went into the plumbing.

Key Findings

None of the three is prompt injection, and the distinction changes who owns the fix

Simon Willison added the classification after a reader pointed out that Rehberger’s chain never involved the model following malicious instructions from the website. His update: “this is more of a confused environment attack where the nature of the environment that the agent is exposed to results in an exploit”. Manifold Security reached the same conclusion about GitSpawn from the other direction. “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”.

The three cases side by side:

GitSpawnAuto-mode zip breakInstruction privilege escalation
Trusted materialA repository’s .git/configA struct.py in an extracted archiveTool output re-labelled by the harness
What executed itGit, refreshing its indexPython, resolving import struct from the current working directoryThe model, reading attacker text at user or system level
Where the model wasNot yet called (Claude Code, goose, Qwen Code)Writing a decoder it chose to writeFollowing what it believed was the user’s task
Control that failed to see itTrust prompt, sandbox, approval, all downstreamAuto-mode classifier judging each command in isolationAutomatic permission review reading the reconstructed history
ResultCode runs with the user’s privileges, outside the sandbox3/5, 3/5, and 4/5 runs across three variants13 of 13 objectives on 6 of 6 harnesses
SourcesManifold, The Hacker NewsRehbergerHe et al.

The ownership consequence follows. Prompt injection is a model problem with harness mitigations; a confused environment is a harness problem the model cannot fix. Four vendors patched GitSpawn without a model change, per Manifold’s table, and Anthropic answered Rehberger by pointing at OS isolation rather than the classifier. The controls therefore belong in the harness’s startup path and process boundary, not in a better-tuned reviewer.

The setup material an agent trusts by default is a taxonomy, and most of it predates agents

Every item below is something an IDE, a version-control tool, or an interpreter reads and acts on when a directory is opened. Agents inherited all of it.

Five kinds of setup material a coding agent trusts by default, each executed by a different layerA taxonomy of setup material that reaches execution without a model decision. Git config callbacks run inside Git. Archives deliver both Git configs and shadowing modules. Project dotfiles run as hooks or helpers before trust. The interpreter's own module path loads a planted file. The harness's context reconstruction re-labels tool output as user or system instructions.Directory arrives:zip, shared drive, syncfolder, USBGit config callbackscore.fsmonitor,core.hooksPath,gpg.program, filtersProject dotfiles.claude/settings.json hooks,apiKeyHelper, .vscodetasksInterpreter pathstruct.py in the workingdirectoryshadows the stdlibExecuted by Giton git status or git diffExecuted by the harnessbefore the trust promptExecuted by Pythonon the agent's own nextstepTool outputHarness contextreconstructionsubagent task, persistentgoal,scheduled taskRead by the modelat user or system level
Five kinds of setup material a coding agent trusts by default, each executed by a different layerA taxonomy of setup material that reaches execution without a model decision. Git config callbacks run inside Git. Archives deliver both Git configs and shadowing modules. Project dotfiles run as hooks or helpers before trust. The interpreter's own module path loads a planted file. The harness's context reconstruction re-labels tool output as user or system instructions.Directory arrives:zip, shared drive, syncfolder, USBGit config callbackscore.fsmonitor,core.hooksPath,gpg.program, filtersProject dotfiles.claude/settings.json hooks,apiKeyHelper, .vscodetasksInterpreter pathstruct.py in the workingdirectoryshadows the stdlibExecuted by Giton git status or git diffExecuted by the harnessbefore the trust promptExecuted by Pythonon the agent's own nextstepTool outputHarness contextreconstructionsubagent task, persistentgoal,scheduled taskRead by the modelat user or system level
Setup material reaches execution through four different layers, only one of which is the model; the delivery vector on the left is anything that moves a directory rather than cloning it.

Git config callbacks. Justin Steven’s March 2022 advisory catalogued the keys whose values are commands: core.fsmonitor, core.gitProxy, core.sshCommand, diff.external, and the pager settings, all readable from a repository’s own .git/config (Steven). Git’s reference says core.fsmonitor “contains the pathname of the ‘fsmonitor’ hook command” when it is not a boolean, invoked by commands that refresh the index. Sonar’s April 2026 write-up added a chained pair, log.showSignature with gpg.program, which fired through the git log Claude Code ran pre-trust in 2.0.50 (Sonar).

Archives. The zip is the common delivery vector across two of the three cases, for different reasons. Manifold is precise that GitSpawn does not travel by clone: “Cloning a hostile URL does nothing, and neither does fetch or pull”, because a clone does not copy the source’s local config. The repository has to arrive as files with .git inside. Rehberger’s archive carried no Git config at all; it carried a module that would sit next to the agent’s script.

Project dotfiles. Sonar’s third finding was that .claude/settings.json offered two pre-trust execution paths, the apiKeyHelper setting and hooks configured to run before the trust dialog, fixed in 2.0.71 on December 16, 2025 (Sonar). VS Code’s Restricted Mode names the wider family for editors: tasks, debug configurations, and workspace settings containing executable paths are all disabled until the folder is trusted (VS Code).

The interpreter’s own path. Python’s documentation states that “the first entry in the module search path is the directory that contains the input script, if there is one”. Any script the agent writes and runs inside an extracted directory, or any inline python -c program it runs from there, therefore resolves import struct against that directory first. This is not a bug, and it is why the attack survives a model that refused to run the archive’s binary.

Harness context reconstruction. The paper’s category is different in kind: the material is not a file but a label. When a main agent delegates, the harness places the task into the subagent’s context as a user-level message; persistent goals and scheduled tasks reintroduce tool-level content the same way (He et al.).

Package manifests, lockfiles, and build scripts belong on this list by construction, since a harness that runs npm install or make on open executes whatever they name. None of the three disclosures exercised that path, and public reporting on agents firing install scripts pre-trust remains thin.

GitSpawn is a 2021-class IDE bug, in agents that run Git before they ask

The history is what makes GitSpawn embarrassing rather than novel. Steven’s advisory records that VS Code fixed the class as CVE-2021-43891 in 1.63.1 by requiring workspace trust before enabling Git integration, and that JetBrains fixed it in 2021.3.1 by disabling version control in untrusted projects (Steven). Git’s maintainers told him “no fixes or mitigations are currently planned”, then shipped the opt-in safe.bareRepository in 2.38.0 that October.

Four years later, the agents made the same mistake in the same order. Claude Code ran git status before the workspace-trust prompt was accepted, goose ran git diff before contacting a model, Qwen Code ran git status before the user had authenticated, and Grok Build gathered Git context on the first keystroke (Manifold). The goose advisory states the consequence plainly: “So running goose review inside a malicious repo runs attacker code - no submitted prompt, no model call, no tool approval, no trust prompt”.

For Claude Code this was a regression. Sonar’s April report noted that Anthropic had already moved the startup sequence once, and The Hacker News quotes its finding: “In version 2.0.34, Claude was updated in a way that mitigated the specific vulnerability by no longer running git status before the user approved the trust dialog. However, a related issue persisted”. That release shipped on November 5, 2025. Manifold found the same startup behaviour present again in 2.1.193, which shipped on June 25, 2026 (The Hacker News).

The GitSpawn payload fires before every control in the agent's startup sequenceA coding agent's startup sequence in order. The directory is opened, the agent runs git status or git diff for context, Git executes the repository's core.fsmonitor command with the user's privileges, and only afterwards do the trust prompt, the first model call, the sandbox, and the permission classifier come into play.Open directoryAgent runsgit status / git diffGit executescore.fsmonitor commandTrust promptFirst model callSandbox andpermission classifier
The GitSpawn payload fires before every control in the agent's startup sequenceA coding agent's startup sequence in order. The directory is opened, the agent runs git status or git diff for context, Git executes the repository's core.fsmonitor command with the user's privileges, and only afterwards do the trust prompt, the first model call, the sandbox, and the permission classifier come into play.Open directoryAgent runsgit status / git diffGit executescore.fsmonitor commandTrust promptFirst model callSandbox andpermission classifier
In the affected agents the config callback ran at step three, before the trust prompt, the model, the sandbox, or the classifier existed for the session; adapted from Manifold Security’s per-agent timing.

The patch state, per Manifold’s table and The Hacker News’s September 2 report, is uneven and dated:

AgentReported to vendorStatus at the September 1 retest
Claude Code, core.fsmonitor pathJune 26, 2026Patched in 2.1.196 (affected: 2.1.193)
Claude Code, ultrareview pathJuly 15, 2026Unpatched in 2.1.252
gooseJuly 13, 2026Patched in 1.44.0 (CVE-2026-72718)
Codex CLIJuly 20, 2026 (closed as duplicate of an earlier report)Patched in 0.131.0 (CVE-2026-19592, affected 0.102.0–0.130.0)
CursorJuly 8, 2026Patched
Hermes AgentJuly 20, 2026Unpatched in 0.21.0 (CVE-2026-71963)
Qwen CodeJuly 7, 2026Unpatched in 0.22.3
Grok BuildJuly 14, 2026Unpatched in 1.0.13

Report dates and retest are Manifold’s (Manifold); the Codex versions and CVE assignments are as reported by The Hacker News, which also notes no entry in CISA’s Known Exploited Vulnerabilities catalog as of September 2, and that CVE-2026-71963 was assigned by VulnCheck according to Manifold but had no published record in MITRE’s CVE List that day. Anything here may have moved since.

The second Claude Code path shows how a denylist fix ages. Manifold says it is “Not core.fsmonitor. It is a different git setting of the same kind, one the review path does not strip”, and it runs on startup before the trust prompt is shown.

Anthropic had already fixed a sibling in June. CVE-2026-55607 combined worktree path confusion with fsmonitor execution during worktree operations to overwrite files such as .zshenv outside the Seatbelt sandbox, patched in 2.1.163 (Anthropic advisory). That one, per the advisory, required cloning a repository that also carried prompt-injection content; the September class needs neither.

OpenAI’s CVE record for Codex, as quoted by The Hacker News, states the helper “runs outside Codex’s command sandbox and without a user-approval prompt, allowing attacker-controlled code to run with the user’s privileges”.

The difference between the controls is Git’s configuration precedence. Manifold’s vendor guidance is to “Sanitize the git config on the context-gathering calls your product makes in the background, for example git -c core.fsmonitor=false status. A -c flag is command scope, which overrides repository scope, so it works per invocation. The cost is that it is a denylist of keys: the ultrareview finding is what happens when one background call misses one key, and Bromure, a VM-isolation vendor, notes that a denylist of this kind needs ongoing maintenance (Bromure).

GitHub’s fix for the Copilot CLI variant (CVE-2026-45033, a nested bare repository discovered during directory traversal) used the environment instead. The patch injects safe.bareRepository=explicit through GIT_CONFIG_COUNT and its GIT_CONFIG_KEY_* / GIT_CONFIG_VALUE_* pairs, which “prevents git from automatically discovering and using bare repositories during directory traversal”. That value closes the buried-bare-repository variant and nothing else: Git’s reference says the setting “Specifies which bare repositories Git will work with”, and explicit limits those to ones named by --git-dir or GIT_DIR, which leaves a non-bare repository’s own .git/config, Manifold’s vector, untouched.

What carries over is the mechanism. Git’s reference documents those variables as overriding config files while yielding to explicit -c options (git-config). Set once in the harness’s subprocess environment, with core.fsmonitor=false and the other callback keys as the values, they cover every Git call the harness makes, including the ones a future feature adds.

Two widely shared defenses do less than they appear to. The first is git config --global core.fsmonitor false, which The Hacker News lists among the mitigations. A practitioner who planted the config and tested it reports the marker file still appeared, because the repository’s local config overrides the global one: “The defense everyone is sharing only protects you against repos that forgot to be malicious”. That is one test on one machine, but it matches Git’s documented precedence, and nothing in the reference marks core.fsmonitor as protected.

The second is safe.directory. Git’s own guard is real: “Git will refuse to even parse a Git config of a repository owned by someone else, let alone run its hooks”. But a zip you extract is owned by you. The ownership check catches a directory dropped by another user on a shared machine and nothing else in the GitSpawn vector.

Git’s safe.* settings are honoured only from protected configuration, meaning the system, global, and command scopes, precisely so that “untrusted repositories” cannot flip them (git-config). The model to copy is that allowlist of scopes, not a denylist of keys. The Git reference documents no switch that ignores a repository’s local config wholesale.

Claude Code’s Bash sandbox shows the other half of the problem. Its protected paths deny writes to hooks and config inside .git, and on Linux and WSL2 the sandbox deletes a top-level HEAD, objects, or refs that appears while a sandboxed command runs, a direct defense against an agent’s own command creating a bare repository (Claude Code docs).

That protects against poison the agent writes. It does nothing about poison that arrived with the folder, and the docs are explicit that “The sandbox isolates Bash subprocesses”: the background git status the harness runs on its own account is not a Bash tool call.

Auto mode’s classifier judged every step correctly, and the chain still landed

Rehberger’s task was a one-line summarization request. The site answered with a 415, Claude Code fell back to curl, received a zip, refused to run the decoder binary it contained, and wrote its own Python decoder instead. It ran that decoder from inside the extracted directory, where the planted struct.py shadowed the standard-library module base64 imports (Rehberger). The refusal was the exploit. Each step was defensible on its own, and that is the property the classifier is built around.

The success rates are small-sample and Rehberger says so: 3/5 for the command-and-control variant, 3/5 and 4/5 for two variants that spawned a second headless Claude Code, which he calls “small samples, not a universal ASR measurement”. The 80% figure that circulated is the 4/5 variant.

Why the classifier could not see it is in Anthropic’s own documentation. The classifier sees user messages, tool calls other than read-only lookups, and CLAUDE.md content; “Tool results are stripped, so hostile content in a file or web page can’t manipulate it directly”. That design is correct for prompt injection.

It also means the classifier evaluates an inline python3 -c decoder without the directory listing that would show struct.py sitting in the working directory, and it has no model of Python’s import order at all. Blake Crosley’s read is that a classifier “evaluates an action against the session’s intent” and cannot assess a consequence that emerges from earlier steps.

The cleanup denial is the finding that should reset expectations. In some runs the model noticed the compromise and tried to kill the malware process, and “Auto Mode denied the cleanup command”. A classifier tuned to refuse dangerous-looking commands refused a kill, after allowing the sequence that made the kill necessary. Any remediation an agent attempts on its own looks, to a per-command reviewer, like the destructive action it exists to stop.

Anthropic closed the report as Informative. Per Rehberger, the company called auto mode “a convenience feature backed by a best-effort classifier, not a security guarantee”, and The Register, which got no reply of its own, reports Anthropic told Rehberger the model’s “behavior is working as designed”.

The documentation now matches. Auto mode is the built-in starting mode on Pro, Max, and Team plans, and its own guidance table lists the isolation it requires as none, with a sandbox or container as defense in depth (Claude Code docs). An independent stress test earlier this year found auto mode missed 81% of risky actions on a synthetic adversarial workload, partly because a third never reached the classifier. Rehberger’s chain reached it every time and passed anyway.

The paper generalizes it: the harness re-labels provenance, and the reviewer believes the label

He, Chen, Qian, Wei, and colleagues define three levels, “tool < user < system-effective”, and show that harnesses move content between them. Instruction hierarchy is a model-side defense that privileges instructions by source; “This construction can elevate low-level content to a higher instruction level and grant it greater model-facing privilege”. The named attack is instruction privilege escalation.

A harness elevates tool-level content to user level when it delegates to a subagentAttacker text enters as tool output at the lowest privilege level. The main agent delegates a task that carries that text. The harness writes the delegated task into the subagent's context as a user-level message. The subagent, and the automatic permission reviewer, now read attacker text as user authorization and execute what the main agent would have refused.Attacker textarrives as tool output(tool level)Main agentdelegates a taskHarness writes the taskinto the subagent's contextas a user messageSubagent reads itat user levelPermission reviewer seesthe user explicitly approvedthisAction executes
A harness elevates tool-level content to user level when it delegates to a subagentAttacker text enters as tool output at the lowest privilege level. The main agent delegates a task that carries that text. The harness writes the delegated task into the subagent's context as a user-level message. The subagent, and the automatic permission reviewer, now read attacker text as user authorization and execute what the main agent would have refused.Attacker textarrives as tool output(tool level)Main agentdelegates a taskHarness writes the taskinto the subagent's contextas a user messageSubagent reads itat user levelPermission reviewer seesthe user explicitly approvedthisAction executes
Delegation is the elevation step: the harness’s own context construction turns tool output into a user-level instruction, and the reviewer inherits the wrong label; adapted from He et al., arXiv:2608.27299.

The evaluation covered Claude Code 2.1.210, Codex 0.138.0, Gemini CLI 0.50.0, Qwen Code 0.21.4, Kimi 0.36.0, and OpenCode 1.18.1, against 13 objectives spanning secret exfiltration, a Git proxy hijack, a malicious CA install, database and toolchain deletion, a command-and-control shell, and an SSH authorized-key backdoor (He et al.).

Under full-access execution the attacks achieved all 13 on all six, with per-harness success rates from 31.7% to 100%. Across both permission modes, tool-to-user escalation averages 97.3% once escalation succeeds. The tool-to-system variant, which installs a malicious subagent alongside a modified AGENT.md, completed all 13 objectives in every evaluated configuration once the malicious behavior was hidden in a separate script, which the paper summarizes as a 100% success rate; its mean per-attempt rate was 80.3%, ranging from 61.9% to 100% by harness and mode (He et al.).

The permission-review result is the one that connects to Rehberger. Three of the six harnesses offer what the paper calls automatic permission review, mapped to Claude Code’s auto mode, Codex’s Approve-for-me mode, and Qwen Code’s auto mode. “Under automatic permission review, the attacks achieve all 13 objectives on all three harnesses that provide this mode”. The reviewer did not misjudge risk. In the motivating case it classified the action as high risk and authorized it anyway, because “the reconstructed history presents attacker-derived text as user authorization”.

The paper stops there. It proposes no defense, and its root-cause statement is the specification of one: the harness must give each input a placement that conveys its instruction level, with “the role labels faithfully reflect the true provenance of the content”. The finding generalizes past delegation. Persistent goals and scheduled tasks reproduce all 13 objectives too, because each is a harness feature that rewrites yesterday’s tool output as today’s user request.

This is a preprint from one group, with a single harness version each, and the success rates depend on the multi-agent mechanism used to trigger escalation. The all-13-on-all-6 headline is the result to rely on; the percentages are indicative.

Process-level isolation is the control that holds when the classifier is wrong

The parties to the Rehberger disclosure ended in the same place, though by endorsement rather than independent arrival. Rehberger’s closing recommendations, four bullets that Willison reproduces under “I agree with Johann’s conclusion here”, are to “Run unattended coding agents in a container, VM or OS sandbox”, “Restrict network egress”, “Monitor your agents”, and “Do not expose home directories, SSH keys, cloud credentials,… to the agent runtime”. Rehberger adds a principle that reads as a design constraint: “Do not trust the model output”.

Anthropic’s own reply named OS isolation and network egress control as where the security lives (Rehberger). Bromure’s argument is that a disposable VM with decoy credentials and an egress firewall sidesteps the startup-ordering problem entirely, rather than reordering it (Bromure). That is a pitch for its own product, and it lands where the independent voices do.

The same conclusion held for DuneSlide, where Cursor’s sandbox existed and was rewritten from inside: enforcement has to live where the agent’s own actions cannot reach it. GitSpawn is the mirror case, a process the harness spawned before any sandbox applied.

What the built-in sandbox actually covers matters for anyone relying on it. Claude Code’s uses Seatbelt on macOS and bubblewrap on Linux, enforced at the OS level for a Bash command and its child processes, and Anthropic reported an 84% reduction in permission prompts from it internally without stating the method (Anthropic).

Its defaults are narrower than the word suggests. The docs state that default read access covers the whole computer and “still allows reading credential files such as ~/.aws/credentials and ~/.ssh/, that “There is no built-in credential deny list”, that native Windows is unsupported, and that the proxy admits connections by client-supplied hostname without inspecting TLS.

Two settings decide whether it is a boundary. Claude Code’s escape hatch lets the model retry a blocked command with dangerouslyDisableSandbox, and in auto mode the classifier, not you, evaluates that retry; allowUnsandboxedCommands: false removes the hatch (Claude Code docs). And sandbox.credentials mask entries in a repository’s .claude/settings.json are ignored, honoured only from settings you or an administrator control: protected configuration, applied to the harness.

Practical Implications

  • Inventory what your harness reads before its first trust decision, and use VS Code’s Restricted Mode list as the checklist. Workspace Trust exists to let the user “decide whether code in your project folder can be executed” without approval, and it disables tasks, debugging, executable-path settings, and now AI agents until then. Every background call your agent makes before that gate is a GitSpawn candidate.
  • Strip Git config on every background call, and prefer the environment to the flag. git -c core.fsmonitor=false fixes one call and one key; the same override set through GIT_CONFIG_COUNT in the subprocess environment, the mechanism Copilot CLI shipped, covers every call the harness makes. Two rows, not one: for a repository’s own .git/config, override core.fsmonitor and the other callback keys Steven catalogued (core.hooksPath, core.sshCommand, diff.external, gpg.program behind log.showSignature) on every invocation, or do not invoke Git before trust; for buried bare repositories, add safe.bareRepository=explicit, which closes that variant and no other.
  • Do not count git config --global core.fsmonitor false or safe.directory as a fix. The repository’s local config wins over global by design, and the ownership check only fires for directories another user owns. Both belong in a user’s hygiene, neither in a vendor’s threat model.
  • Treat an extracted archive as hostile in a way a clone is not. A repository’s own .git/config arrives by zip, shared drive, sync folder, or USB and never by clone; a bare repository buried in a subdirectory is the exception, and the Copilot CLI advisory lists “A cloned repository that already contains nested bare repositories” among its delivery routes. A harness that handles archives a task asked for should extract them into a directory it will not run interpreters from, and check git config --get core.fsmonitor before opening anything with a .git inside.
  • Set PYTHONSAFEPATH in the agent’s subprocess environment. Python 3.11 added -P and PYTHONSAFEPATH to stop prepending the script’s directory, or the current directory for python -c, to sys.path (Python docs). None of the disclosures lists this mitigation, and it closes only the specific shadowing Rehberger used, but it is one environment variable, and with it the planted struct.py never loads: the decoder’s import struct resolves to the standard library instead. Equivalent path hygiene applies to any interpreter the agent writes scripts for.
  • Keep auto mode as a blast-radius control and put a real boundary under it. Run untrusted-input sessions with allowUnsandboxedCommands: false, a sandbox.credentials deny list covering ~/.ssh, ~/.aws, and token variables, and an egress allowlist that excludes broad hosts like github.com. For anything unattended, use a disposable VM or container per task, as Rehberger and Crosley recommend and Willison endorses.
  • Pre-authorize remediation so the classifier cannot veto it. Rehberger’s cleanup denial means an agent that notices a compromise may be unable to act on it. An allow rule for process termination and file reversion inside the working directory, or a hook that routes those to a human rather than the classifier, keeps the response path open.
  • If you build a harness, never re-label tool output as a user message. The paper’s root cause is provenance loss at delegation, persistence, and scheduling. A subagent task built from tool results should carry a tool-level tag through to the subagent’s context and to any permission reviewer, and a scheduled task should record which level authored it. A reviewer that only sees a user request will approve what the user never asked for.
  • Verify versions rather than trusting the patch headline. Claude Code at or above 2.1.196 closes core.fsmonitor and 2.1.163 closes the worktree sibling, but the ultrareview path was open in 2.1.252 at the retest; goose needs 1.44.0, Codex CLI 0.131.0. Hermes Agent, Qwen Code, and Grok Build were unpatched at the versions Manifold tested. Check the vendor’s current advisory before assuming any of these has moved.

Open Questions

  • Will Git grow a scope-level switch for untrusted repositories? The maintainers’ 2022 position was that dangerous config keys are a feature, and the safe.* settings that followed protect specific cases (ownership, bare repositories) rather than local config as a class. Whether agent-driven demand for an ignore-local-config mode changes that is unresolved; no such switch appears in the current reference.
  • How much of the confused-environment surface is interpreter path behavior? Python’s sys.path[0] is documented and has a switch. Whether Node’s resolution of a planted node_modules, Ruby’s load path, or shell PATH inside an extracted directory offer the same shadowing to an agent’s self-written scripts has not been reported against a coding agent yet.
  • Does provenance labelling survive compaction and persistence in practice? The paper shows harness features re-labelling content at delegation and scheduling time. Whether a provenance tag attached at ingestion survives summarization, memory writes, and cross-session resumption without being flattened to “user said” is the engineering question its root-cause statement leaves open.
  • Can a classifier be taught to see the environment? The auto-mode classifier strips tool results by design. A reviewer given a directory listing or an interpreter’s resolved import path as application-provided context, which Claude Code’s hooks now allow, might have caught Rehberger’s chain; whether that re-exposes the reviewer to hostile content is untested in public.

Sources

  1. Manifold Security, “GitSpawn: A Single Flaw Lets Untrusted Repos Run Code in Claude Code, Codex, Cursor, and Grok”
  2. The Hacker News, “Malicious .git Configs Can Make Claude, Codex, Cursor, and Other AI Agents Run Attacker Code”
  3. goose security advisory GHSA-r5pp-p5r8-466r, “Arbitrary command execution in goose CLI via goose review via git core.fsmonitor” (CVE-2026-72718)
  4. Anthropic Claude Code security advisory GHSA-7835-87q9-rgvv, “Sandbox Escape via Git Worktree Path Confusion” (CVE-2026-55607)
  5. GitHub Copilot CLI security advisory GHSA-9ccr-r5hg-74gf, “Nested Bare Repository Can Execute Arbitrary Commands via core.fsmonitor” (CVE-2026-45033)
  6. Sonar, “Arbitrary code execution and Claude Code CLI: How Claude executed code before you click ‘trust’”
  7. Justin Steven, “Git honours embedded bare repos, and exploitation via core.fsmonitor in a directory’s .git/config affects IDEs, shell prompts and Git pillagers” (2022 advisory)
  8. Git reference, git-config: scopes, protected configuration, and GIT_CONFIG_* environment variables
  9. Git source, Documentation/config/safe.adoc (safe.directory, safe.bareRepository)
  10. Git source, Documentation/config/core.adoc (core.fsmonitor, core.hooksPath, core.sshCommand)
  11. jamilxt, “A Malicious Repo Can Now Run Code Before Your AI Agent Shows a Trust Prompt. I Verified the Defenses That Actually Work”
  12. Bromure, “git status ran their code”
  13. Simon Willison, “Breaking Claude Code Opus 5 Auto Mode”
  14. Johann Rehberger, “Breaking Claude Code Opus 5 and Auto Mode” (Embrace The Red)
  15. The Register, “Researcher shows how Claude Code can be tricked simply by asking it to summarize a website”
  16. Blake Crosley, “Claude Code Auto Mode Is Not a Security Boundary”
  17. Python documentation, “The initialization of the sys.path module search path”
  18. Python documentation, “Command line and environment” (-P, PYTHONSAFEPATH)
  19. Claude Code docs, “Choose a permission mode”
  20. Claude Code docs, “Configure the sandboxed Bash tool”
  21. Anthropic Engineering, “Beyond permission prompts: making Claude Code more secure and autonomous”
  22. He, Chen, Qian, Wei, et al., “When Context Gets Root: Privilege Escalation in LLM Harnesses,” arXiv:2608.27299
  23. VS Code documentation, “Workspace Trust”