[FEATURE] Expose allowGitConfig from sandbox-runtime in settings schema

Open 💬 2 comments Opened May 11, 2026 by pento

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

sandbox-runtime hardcodes a deny on .git/config writes (macos-sandbox-utils.ts#L67-L71 @ bdb4ea6):

// Git config - conditionally blocked based on allowGitConfig setting
if (!allowGitConfig) {
  denyPaths.push(path.resolve(cwd, '.git/config'))
  denyPaths.push('**/.git/config')
}

The override option (allowGitConfig) was added by @alo-ant in commit a544fe7 for "git remote URL updates for proxy authentication", and is exposed across both Linux and macOS sandbox utilities. It just isn't surfaced in Claude Code's settings.json schema, so there's no user-configurable way to enable it.

The symptom: with push.autoSetupRemote = true set globally, every first push of a new branch from a sandboxed Claude Code session emits:

remote:      https://github.com/<owner>/<repo>/pull/new/<branch>
To https://github.com/<owner>/<repo>.git
 * [new branch]      <branch> -> <branch>
error: could not write config file .git/config: Operation not permitted
error: could not write config file .git/config: Operation not permitted

The push itself succeeds; only the local upstream tracking write (branch.<name>.remote + branch.<name>.merge) fails. Subsequent pushes from the same branch then need -u or a manual git branch --set-upstream-to=... to re-establish tracking, which is awkward in agent-driven workflows.

Proposed Solution

Surface the existing allowGitConfig option in the settings.json schema:

{
  "sandbox": {
    "filesystem": {
      "allowGitConfig": true
    }
  }
}

Default false, preserving current behaviour. The sandbox-runtime API already accepts this parameter, this is purely a settings-schema and pass-through change in Claude Code.

.git/hooks should remain protected at the sandbox layer even when allowGitConfig: true, matching the existing upstream design intent.

Alternative Solutions

Workarounds attempted from the user side (all fail, since no user allowWrite entry can override the hardcoded sandbox-runtime deny):

  • .git/config in sandbox.filesystem.allowWrite: resolves to ~/.claude/.git/config (relative to settings.json), doesn't match real repo paths.
  • Absolute literal path like /Users/<user>/Projects/<owner>/<repo>/.git/config: still blocked.
  • Glob ~/Projects/**/.git/config: appears in the rendered sandbox allowOnly list but is still blocked.

If a simple boolean feels too coarse, more granular alternatives the implementation could take:

  • allowGitConfig: "branch-and-remote-only": sandbox-runtime parses writes and only permits branch.*, remote.*, submodule.* keys. More work upstream.
  • Pass-through of arbitrary sandbox-runtime options under sandbox.filesystem.runtimeOptions — generic escape hatch for power users.

Either is fine; the simple boolean covers ~95% of the actual friction and matches what sandbox-runtime already implements.

Priority

Medium - Would be very helpful

Feature Category

Configuration and settings

Use Case Example

  1. Set push.autoSetupRemote = true in your global ~/.gitconfig (a common recommendation; eliminates needing -u on first push of every branch).
  2. Open claude in a repo under your user's project tree.
  3. Ask Claude to make a change on a new branch and push it: "create a branch fix/something, commit my changes, push it, and open a PR."
  4. Claude runs git checkout -b fix/something, commits, then git push. The push succeeds (branch is created on origin, PR can be opened), but the terminal output includes:

``
error: could not write config file .git/config: Operation not permitted
error: could not write config file .git/config: Operation not permitted
``

  1. The local branch has no upstream tracking. Any subsequent git push from Claude or the user requires -u again, or a manual git branch --set-upstream-to=origin/fix/something. Repeated across every new branch in every Claude session.

Additional Context

Risk model

.git/config is a real persistence vector (core.hooksPath, credential.helper, alias.<name> = !shell, include.path, url.<base>.insteadOf, gpg.program, core.sshCommand). Defaulting the override off is correct.

But the writes git itself performs during normal operation (branch tracking, remote URL changes via git remote, submodule.<name>.url on git submodule add) don't touch any of those vectors. The dangerous additions come from explicit git config <key>, direct file edits, or echo … >> .git/config redirects, all of which can be caught at the tool layer.

Users who want a safer middle ground can layer defense-in-depth on top:

  • PreToolUse hook on Bash|Edit|Write: blocks the obvious attack shapes before they reach the file: pattern-match git config <key> invocations against an allowlist, refuse direct Edit/Write targets that resolve to .git/config, refuse Bash redirect operators (>, >>, tee, sed -i) writing into the same path.
  • PostToolUse hook on Bash|Edit|Write: backstop that parses each project's .git/config with git config --list --file against a key allowlist, and fails if anything outside the allowlist landed (catches whatever slipped past the PreToolUse — e.g. dangerous keys written by a script the agent invoked rather than wrote inline).

I've prototyped both, I'm happy to share patterns if useful as a reference for users adopting this once allowGitConfig is surfaced. The upstream commit's existing design already separates the two layers correctly: .git/hooks stays protected at the sandbox layer even when allowGitConfig: true.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗