Affected: goose (≤1.43.0), OpenAI Codex CLI (0.102.0–0.130.0), Claude Code (2.1.193–2.1.252, plus the
claude ultrareview code path still unpatched at
publication), Hermes Agent (0.18.2, 0.21.0), Qwen Code
(0.19.6, 0.22.3), Grok Build (0.2.93, 1.0.13), Cursor CLI.
CVEs: CVE-2026-72718 (goose, CVSS 7.0), CVE-2026-19592 (Codex), CVE-2026-55607 (Claude Code), CVE-2026-71963 (Hermes Agent).
PRISM defense shipped in: v1.0.0-rc.3 (2026-09-02) — across all seven CLIs.
GitSpawn-class defense: 2026-09-02 advisory
A malicious repository delivered as a zip, USB stick, cloud-sync
folder, or shared drive can carry core.fsmonitor =
<attacker command> in its .git/config.
Any subsequent git call — git status,
git rev-parse, git diff, worktree operations —
triggers git to read the local config and run the attacker's command
as the operator, BEFORE any sandbox, prompt, or approval gate fires.
The threat
The class of attack — the GitSpawn wave — was disclosed
in early September 2026. The first agent hit was goose
(CVE-2026-72718, CVSS 7.0, GitHub advisory
GHSA-r5pp-p5r8-466r, crediting Francisco Rosales). Codex followed
(CVE-2026-19592), then Claude Code
(CVE-2026-55607, fixed in 2.1.196 — but the
claude ultrareview code path was still unpatched on the
last-reported 2.1.252), then Hermes Agent
(CVE-2026-71963, assigned by VulnCheck).
The mechanism is identical across every affected agent: a repo
arrives with a hostile .git/config, the agent runs
git in the background to determine branch state and
changed files, git reads the local config, the attacker's command
fires. For goose specifically, the only mitigation in the affected
versions was stripping the --c core.quotePath=off
flag — every other config key was untouched.
The dangerous keys
| Section / key | When git runs it | Why dangerous |
|---|---|---|
core.fsmonitor | every git status / git diff / rev-parse | the primary vector — git runs the value as a command |
core.hooksPath | every hook event | git loads hooks from this directory; an attacker can plant executable hooks there |
core.sshCommand | every SSH transport call | command substitution; can spawn arbitrary processes |
core.gitProxy | every proxy-aware transport | command substitution |
init.templateDir | every git init in that repo | git clones hooks into every newly-initialised repo |
filter.<name>.clean / .smudge / .required | every git add / git checkout / git clone | filter drivers run on every blob |
attr.tree | every repo walk | attribute-filter injection can rewrite paths and content |
The first one is enough for the headline attack. The rest are adjacent surfaces that PRISM scrubs by the same mechanism.
PRISM's defense — three layers
Layer 1: auto-scrub in every CLI
Every PRISM command that touches a repo auto-scrubs the LOCAL
.git/config of the target before any other
git call. Hard-fails with exit 1 if the scrub
reports errors.
prism-harness init— scrubs before the doctrine scaffold.prism-harness check— surfaces agit-config-safetyline in the report.prism-graf init— scrubs before the graf init spawn.prism-graf doctor— surfacesgit-config-safety.prism-loop doctor— scrubs before spawningprism-doctor.cjs, which callsgitinternally. This is the highest-risk surface — the doctor's very firstgitcall would have triggered fsmonitor in any untrusted repo. The doctor now also pins itscwdto the target, so it can't pick up a hostile config from the operator's shell pwd.prism-route smoke,prism-proxy smoke— scrub the cwd's LOCAL config before spawning the smoke script.
The scrub itself passes
-c core.fsmonitor=false -c core.hooksPath= -c core.sshCommand= -c core.gitProxy=
on every internal git call. The scrub cannot trigger
an attack mid-run.
Layer 2: standalone scrub subcommand
Every PRISM CLI exposes a scrub subcommand for ad-hoc use:
# 1. Inspect, no change.
prism-harness scrub /path/to/untrusted-repo --dry-run
# 2. JSON output for CI gates / supply-chain checks.
prism-harness scrub /path/to/repo --json
# 3. Actually remove the dangerous keys from the LOCAL .git/config.
prism-harness scrub /path/to/untrusted-repo
# 4. Same scrubber on every CLI.
prism-graf scrub /path/to/repo
prism-loop scrub /path/to/repo
prism-sober scrub /path/to/repo
prism-route scrub /path/to/repo
prism-proxy scrub /path/to/repo
The scrubber:
- removes
core.fsmonitor,core.hooksPath,core.sshCommand,core.gitProxy,init.templateDir, and allfilter.<name>.{clean,smudge,required}keys, - reports every value that was removed, on stderr (can't be silenced by piping stdout),
- touches only the LOCAL config — never
--global, never--system, - is idempotent (running twice is a no-op),
- exits 1 if it cannot read the config (fail-closed).
Layer 3: pinned installer
install.sh supports
--verify <minisign.pub> (and
PRISM_VERIFY=<minisign.pub> under pipe mode).
The installer fetches install.sh.sig alongside the
script and aborts with exit 4 on signature mismatch,
before any installer logic runs. Handles both pipe mode
(re-downloads, verifies, then exec bash the verified
copy) and local mode (verifies the on-disk file).
# One-time: fetch the sovereign install pubkey from a trusted channel.
curl -fsSL https://git.sovereign-society.org/prism/prism-harness/raw/branch/main/install.sh.pub \
-o ~/.secrets/prism-install.pub
# Every install: verify before running.
curl -fsSL https://git.sovereign-society.org/prism/prism-harness/raw/branch/main/install.sh \
| sh -s -- --verify ~/.secrets/prism-install.pub --yes
The defense is incomplete without this layer — compromise of the install script itself is full machine compromise otherwise. Install minisign and pin whenever you can.
Operator workflow for untrusted repos
- Don't open the untrusted directory in any AI agent yet.
- From a clean shell, run
prism-harness scrub /path/to/untrusted-repo. - Read the stderr report. Confirm every removed value.
- Optionally verify with
--dry-runfirst; the scrubber reports every key it would remove. - Open the repo in your AI agent.
For CI / supply-chain checks, integrate the scrub into your dependency-import pipeline. The scrub is deterministic, idempotent, and has a JSON output mode:
# CI gate: fail the build if a vendor-supplied repo carries a dangerous key.
prism-harness scrub vendor/ --json \
| jq -e '.removed | length == 0' \
|| { echo "vendor repo carries dangerous git config"; exit 1; }
Scope and non-scope
In scope — the LOCAL .git/config of any
repo PRISM touches. We never touch ~/.gitconfig or the
system config; you can run git config --global in
whatever state you want.
Out of scope — the AI agents PRISM shapes (Claude
Code, Codex, OpenCode, Hermes). They have their own fsmonitor
patches in this same release window. Apply their upstream
core.fsmonitor=false fix, run our scrub, and you have
layered defense. PRISM cannot patch the agents themselves; that's
the agents' maintainers' work.
Out of scope (deferred) — a fully
content-addressed install (no curl | sh at all),
Graf-side core.fsmonitor-equivalent scrubbing (Graf
has no equivalent trust-on-open problem), removing the git-fallback
bridge entirely.
Reporting a GitSpawn finding against PRISM
The defense is fail-closed and well-tested, but if you find a bypass, please report it. The PRISM SECURITY.md doctrine applies:
- Email:
[email protected](PGP key inKEYS) - Forge issue: git.sovereign-society.org/prism — SECURITY-tagged; triaged within 48h
- Out-of-band: Markus Maiwald directly
Include: affected repo + commit SHA, reproduction steps (local; no need to phone home), whether the issue is exploitable on a default install, and a suggested fix if you have one. We credit reporters in the release notes unless you ask for anonymity.
Timeline
- 2026-09-02 — PRISM v1.0.0-rc.3 ships GitSpawn-class
defense in all seven CLIs; this advisory published;
install.sh --verify <minisign.pub>lands on the sovereign forge; npm registry + AUR updated. - Earlier — CVE-2026-72718 disclosed for goose, followed by -19592 (Codex), -55607 (Claude Code), -71963 (Hermes).