fix: drop the Codex CLI .git/config read carve-out and keep only .git/** write#2294
Merged
dyoshikawa merged 3 commits intoJul 17, 2026
Merged
Conversation
…/** write
Everyday git commands (git remote add/set-url, git push -u, local-scope
git config, clone/submodule flows) write to .git/config, so the default
'".git/config" = "read"' rule broke basic workflows while the
protection it added was already partial by design (.git/hooks/ stays
writable). The default carve-out is now '".git/**" = "write"' only;
stricter users can author their own read override in the canonical
permissions.
Also extend the FAQ's codexcli.permission example with the practical
workspace write set ('.', '.git/**', '.agents/**', '.codex/**') since a
tool-scoped category replaces the shared one wholesale and Codex's
:workspace baseline keeps .git, .agents, and .codex read-only. The
workspace-root entry uses '.' rather than './**' because Codex rejects
subpath keys containing '.' components.
Closes #2279
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Warn about the security trade-off of granting .codex/** and .agents/** write access in the FAQ example (sandbox self-reconfiguration and persistent instruction injection). - Note the round-trip exception for the ".git/**" write rule matching the default carve-out, and how to restore it after opting out. - Restore the description of what the dropped .git/config guard protected against (core.fsmonitor / core.hooksPath). - Add a migration note: legacy configs carrying '".git/config" = "read"' now import as a user-authored rule that must be deleted manually to get the writable default. - Singularize remaining 'carve-outs' test names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The advice to author .git rules canonically when git_write_rules is false claimed they round-trip normally, but import cannot distinguish a user-authored '".git/**" = "write"' from the default carve-out and skips that exact pair, contradicting the FAQ's exception note. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
|
@dyoshikawa Thank you! |
dyoshikawa
deleted the
resolve-scrap-issue-2279-codex-git-config-writable
branch
July 17, 2026 02:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Drops the default
".git/config" = "read"carve-out that rulesync emitted into the Codex CLI managed profile's:workspace_rootstable, keeping only".git/**" = "write".In practice git is not usable without write access to
.git/config: everyday commands such asgit remote add/set-url,git push -u(recordsbranch.<name>.remote/merge), local-scopegit config, and clone/submodule flows all write to it. Meanwhile the protection the read guard added was already partial by design (.git/hooks/stays writable, a maintainer decision on #2272), so the cost/benefit did not hold. Making it worse, the default was injected per exact key (??=), so even a user-authored".git/**": "allow"write rule could not suppress it — Codex resolves the more specific path with priority, keeping.git/configread-only against the user's explicit intent.Users who want stricter isolation can still author their own override (e.g.
read: { ".git/config": "allow" }) in the canonical permissions; thecodexcli.git_write_rules: falseopt-out stays as is.Changes
src/features/permissions/codexcli-permissions.ts: remove".git/config": "read"fromCODEX_GIT_WRITE_RULESand update the rationale comments (generate and import both key off the same constant, so the import-skip logic needed no code change).src/types/permissions.ts: update thegit_write_rulesdoc comment.codexcli-permissions.test.ts(default emission, import of legacy".git/config" = "read"as a user rule, round-trip) ande2e-permissions.spec.ts.docs/faq.md: rewrite the carve-out paragraph, and extend thecodexcli.permissionexample with the practical workspace write set (".",".git/**",".agents/**",".codex/**") plus an explanatory bullet — a tool-scoped category replaces the shared one wholesale, and Codex's:workspacebaseline keeps.git,.agents, and.codexread-only inside workspace roots (codex-rsFileSystemSandboxPolicy::workspace_write).docs/reference/file-formats.md: rewrite the carve-out paragraphs (no longer claims.git/configstays read-only).skills/rulesync/*: regenerated viascripts/sync-skill-docs.ts.Note: the issue proposed
"./**" = "write"for the workspace-subtree entry, but Codex rejects:workspace_rootssubpath keys containing.or..components (Codex permissions reference — "Other subpaths must be relative descendants and cannot contain.or..components"; the documented base form is"."), so the example uses".": "allow"instead.Closes #2279
🤖 Generated with Claude Code