macOS sandbox: injected GIT_SSH_COMMAND omits the proxy credentials (the Linux branch passes them), so git-over-SSH always fails

Status Open
Reported on v2.1.217
Maintainer reply None cached
Activity 0 comments · opened Jul 29, 2026

Sandbox: injected GIT_SSH_COMMAND uses SOCKS5 without the proxy credentials, so git-over-SSH always fails

Version: Claude Code 2.1.217
Platform: macOS (Darwin 25.3.0), OpenSSH_10.2p1
Sandbox: enabled (sandbox.enabled: true, allowUnsandboxedCommands: false)

Summary

With the sandbox enabled, Claude Code injects

GIT_SSH_COMMAND=ssh -o ControlMaster=no -o ControlPath=none \
  -o ProxyCommand='nc -X 5 -x localhost:<port> %h %p'

-X 5 selects SOCKS5, but no credentials are passed. The sandbox proxy requires
authentication — every other proxy variable in the same environment carries a
user:password pair:

HTTPS_PROXY=http://srt:<hex>@localhost:<port>
ALL_PROXY=http://srt:<hex>@localhost:<port>
FTP_PROXY=socks5h://srt:<hex>@localhost:<port>

macOS nc cannot supply SOCKS5 credentials at all: its -P user flag applies
only to HTTP CONNECT proxies, and it prompts for the password interactively,
so it is unusable from a ProxyCommand even for HTTP. The result is that
every git-over-SSH operation fails inside the sandbox.

Reproduction

$ ssh -o ProxyCommand='nc -X 5 -x localhost:64193 %h %p' -T git@github.com
nc: authentication method negotiation failed
Connection closed by UNKNOWN port 65535

Evidence that the transport itself is fine

An HTTP CONNECT through $HTTPS_PROXY with Basic auth reaches GitHub's SSH
endpoints and returns a genuine banner:

('github.com', 22)      -> HTTP/1.1 200 Connection Established   banner: SSH-2.0-5645aa6
('ssh.github.com', 443) -> HTTP/1.1 200 Connection Established   banner: SSH-2.0-5645aa6

Substituting a CONNECT-speaking ProxyCommand takes ssh -T git@github.com
all the way to Permission denied (publickey) — i.e. the connection succeeds
and only key material is missing. So this is purely a bug in how the
ProxyCommand is constructed, not a network-policy decision.

The Linux branch gets this right — macOS is the outlier

The same function builds the variable for both platforms, and only the macOS
arm omits the credentials:

if (u === "macos")
  s.push(`GIT_SSH_COMMAND=ssh … -o ProxyCommand='nc -X 5 -x localhost:${t} %h %p'`);
else if (u === "linux" && e) {
  let d = n ? `,proxyauth=srt:${n}` : "";
  s.push(`GIT_SSH_COMMAND=ssh … -o ProxyCommand='socat - PROXY:localhost:%h:%p,proxyport=${e}${d}'`);
}

Linux uses socat with an HTTP CONNECT proxy and passes proxyauth=srt:<token>.
macOS uses nc with SOCKS5 and passes nothing. The proxy token (n) is plainly
in scope in both arms — it is simply not used on macOS. This looks like a
straightforward omission rather than a platform limitation.

Two further sandbox interactions worth noting

Even with a working ProxyCommand, two more failures follow from
~/.ssh being in sandbox.filesystem.denyRead:

  1. No identity is loadable — every candidate key logs identity file … type -1

— and SSH_AUTH_SOCK is unreachable too
(ssh_get_authentication_socket: Operation not permitted), so the agent
fallback fails as well.

  1. ~/.ssh/known_hosts is unreadable, so host verification hard-fails with

hostkeys_foreach failed … Operation not permitted /
Host key verification failed — even when the connection itself succeeds.

Neither is fatal to a determined user (a forwarded agent socket via
allowUnixSockets, plus UserKnownHostsFile pointed outside ~/.ssh), but
both mean git-over-SSH cannot work out of the box on macOS regardless of the
ProxyCommand fix.

Suggested fixes (any one)

  1. Emit the SOCKS5 credentials the proxy expects, and use a client that can

actually send them — nc cannot.

  1. Emit an HTTP CONNECT ProxyCommand carrying Proxy-Authorization: Basic,

consistent with HTTPS_PROXY.

  1. Ship a small first-party CONNECT helper and point ProxyCommand at it,

rather than depending on the host's nc, whose proxy support is
platform-dependent (socat, corkscrew, ncat, proxytunnel were all
absent on this machine).

Secondary observation

~/.ssh being in sandbox.filesystem.denyRead is a sensible default, but note
it is undermined in the default configuration: gh auth token succeeds inside
the sandbox and returns a live token whose scopes include repo and
admin:public_key — the latter allows adding SSH keys to the account. A
sandboxed session therefore already holds authority comparable to what the
~/.ssh denial is protecting. Worth documenting, at least.

Workaround

sandbox.network.allowUnixSockets (undocumented as far as I can tell) allows a
path-scoped ssh-agent socket to be forwarded in, which solves key access
without exposing ~/.ssh. Combined with a custom CONNECT ProxyCommand in
core.sshCommand, git-over-SSH works. Both halves are user-supplied
workarounds for what looks like a shipping defect.

View original on GitHub ↗