Curated-plugin startup sync corrupts the host repo when codex runs inside a git hook (inherited GIT_DIR) — isolation fix #29785 is in no release
Version: codex-cli 0.142.3 and 0.142.5 (macOS, arm64). Also affects 0.143.0-alpha.9.
Summary: The curated-plugin startup sync (core-plugins/src/startup_sync.rs) spawns git -C <cache> init / fetch --depth 1 --no-tags … +HEAD:refs/codex/curated-sync / reset --hard / clean -fdx without scrubbing repository-local git environment variables. When codex is launched from inside a git hook (e.g. a post-commit hook that runs codex exec for automated review), git has exported an absolute GIT_DIR for commits made in linked worktrees — and GIT_DIR overrides -C-based discovery in every child git process. The sync then operates on the user's repository:
git init"reinitializes" the host repo withGIT_DIRset and cwd elsewhere → git guesses bare →core.bare=trueis written into the shared config (breaksstatus/pullin the primary checkout).- The fetch grafts
refs/codex/curated-sync+ a.git/shallowfile (anopenai/pluginscommit) into the host repo. - The
reset --hardcan move the user's checked-out branch to the foreignopenai/pluginscommit (reproduced; see below).
Notably, --sandbox read-only does not contain any of this — the sync is codex startup machinery, not sandboxed model commands.
Reproduction: in a repo, install a post-commit hook that runs codex (any prompt); commit in a linked worktree (git worktree add … && git commit …). Observe core.bare=true, refs/codex/curated-sync, and .git/shallow on the host repo. A minimal simulation (hook running the same git sequence codex spawns) shows the worktree branch hard-reset to the fetched commit.
Real-world impact: three developer repos on one machine corrupted over two days (commits from worktree hooks that spawn codex for post-commit review); the branch-reset case is especially dangerous because it's silent when the branch was already pushed.
Fix status: #29785 ("Isolate curated plugin sync Git environment", merged 2026-06-24) fixes exactly this on main — git_command() now env_removes the 16 repository-local GIT_* vars. But it is not contained in any released version: rust-v0.142.5 and rust-v0.143.0-alpha.9 both lack it. Could this be cherry-picked into the next stable release? Given the severity (silent branch reset, bypasses the read-only sandbox), it seems worth expediting.
4 Comments
Thanks for the detailed writeup and repro — the GIT_DIR inheritance angle is
clear. I'll pick this up and work on getting #29785 into a release.
@sallyjones6 @ChetanyaRathi Isolation is definitely important here. It would also help if the execution environment and inherited state were recorded as part of the execution metadata so unexpected workspace mutations are easier to trace back.
This is already fixed in rust-v0.143.0 (out today). I just checked the tagged source, GIT_DIR and the other repo-local GIT_* vars are stripped via env_remove in the curated-sync git command. The repro was on 0.143.0-alpha.9, before #29785 landed; final 0.143.0 has it. Upgrading resolves this, 0.142.x backport only if that line's still maintained.
Confirmed fixed as of the 0.144.0 release line: the #29785 merge commit (134646e) is an ancestor of rust-v0.144.1 (and absent from rust-v0.142.5, matching the vulnerable releases).
Verified live on codex-cli 0.144.1: with plugins force-enabled (
-c features.plugins=true) and codex spawned with an inheritedGIT_DIR/GIT_WORK_TREEpointing at a scratch repo, the repo stays intact — nocore.bareflip, HEAD unchanged, no.git/shallowgraft, norefs/codex/curated-syncref.Closing; the
[features].plugins = falseworkaround is no longer needed on ≥0.144.0.