Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
"name": "rh-dataverse",
"source": "./rh-dataverse",
"strict": true
},
{
"name": "sandbox-ssh-fix",
"source": "./sandbox-ssh-fix",
"strict": true
}
]
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ claude plugin marketplace add cblecker/claude-plugins
| [pr-review-toolkit](./pr-review-toolkit) | Comprehensive PR review board using shared workflow context |
| [gws](./gws) | Google Workspace CLI skills for Gmail, Calendar, Drive, Docs, Sheets, Slides, and Meet |
| [rh-dataverse](./rh-dataverse) | Red Hat Dataverse MCP server |
| [sandbox-ssh-fix](./sandbox-ssh-fix) | Fixes git-over-SSH in the Claude Code sandbox on macOS by replacing the broken BSD nc SOCKS5 proxy |

## License

Expand Down
11 changes: 11 additions & 0 deletions sandbox-ssh-fix/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "sandbox-ssh-fix",
"version": "1.0.0",
"description": "Fixes git-over-SSH in the Claude Code sandbox on macOS by replacing the broken BSD nc SOCKS5 proxy with ncat",
"author": {
"name": "cblecker",
"email": "admin@toph.ca"
},
"keywords": ["sandbox", "ssh", "git", "macos", "workaround"],
"homepage": "https://github.com/anthropics/claude-code/issues/70684"
}
25 changes: 25 additions & 0 deletions sandbox-ssh-fix/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# sandbox-ssh-fix

Workaround for [anthropics/claude-code#70684](https://github.com/anthropics/claude-code/issues/70684).

## Detection and fix logic

The SessionStart hook (`scripts/fix-git-ssh.sh`) runs on every session start and
checks three conditions before acting:

| Condition | Variable | Check |
|-----------|----------|-------|
| Inside sandbox | `SANDBOX_RUNTIME` | `== 1` |
| Broken proxy | `GIT_SSH_COMMAND` | contains `nc -X 5` |
| Auth required | `ALL_PROXY` | contains `@` |

When all conditions are true, the script parses `ALL_PROXY`
(`socks5h://user:pass@host:port`) and writes a corrected `GIT_SSH_COMMAND` to
`$CLAUDE_ENV_FILE`.

The `ncat` path uses `127.0.0.1` instead of `localhost` because ncat resolves
`localhost` differently and the connection is refused.

SSH options `ControlMaster=no` and `ControlPath=none` are preserved from the
original command because SSH mux sockets aren't in the sandbox's allowed Unix
socket paths.
43 changes: 43 additions & 0 deletions sandbox-ssh-fix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# sandbox-ssh-fix

Workaround for broken git-over-SSH in the Claude Code sandbox on macOS.

## Problem

The Claude Code sandbox injects `GIT_SSH_COMMAND` with a `ProxyCommand` using
BSD `nc -X 5` for SOCKS5 proxying. On macOS, `nc` doesn't support SOCKS5
authentication, and the proxy requires auth — breaking all git-over-SSH
operations (`git fetch`, `git push`, `git pull`).

Tracked upstream: <https://github.com/anthropics/claude-code/issues/70684>

## How it works

A SessionStart hook detects the broken pattern and overrides `GIT_SSH_COMMAND`
via `$CLAUDE_ENV_FILE`:

- If `ncat` is available: uses `ncat --proxy-type socks5 --proxy-auth` with
credentials parsed from `ALL_PROXY`
- If `ncat` is not available: falls back to plain `ssh` (bypasses the proxy,
relies on the sandbox network allowlist)

The fix only activates when all conditions are met:

1. Running inside the sandbox (`SANDBOX_RUNTIME=1`)
2. `GIT_SSH_COMMAND` contains the broken `nc -X 5` pattern
3. `ALL_PROXY` contains credentials (has `@`)

## Prerequisites

Install `ncat` (from nmap) for full SOCKS5 proxy support:

```bash
brew install nmap
```

Without `ncat`, the plugin falls back to direct SSH which still works but
bypasses the sandbox's SOCKS5 proxy.

## License

Apache License 2.0. See [LICENSE](../LICENSE) for details.
14 changes: 14 additions & 0 deletions sandbox-ssh-fix/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/fix-git-ssh.sh"
}
]
}
]
}
}
29 changes: 29 additions & 0 deletions sandbox-ssh-fix/scripts/fix-git-ssh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail

# Only run inside the Claude Code sandbox
[[ "${SANDBOX_RUNTIME:-}" == "1" ]] || exit 0

# Only fix the broken BSD nc SOCKS5 proxy pattern
[[ "${GIT_SSH_COMMAND:-}" == *"nc -X 5"* ]] || exit 0

# Only needed when ALL_PROXY has credentials (contains @)
[[ "${ALL_PROXY:-}" == *"@"* ]] || exit 0

# CLAUDE_ENV_FILE is required to export the fix
[[ -n "${CLAUDE_ENV_FILE:-}" ]] || exit 0

# Parse ALL_PROXY: socks5h://user:pass@host:port
proxy_auth="${ALL_PROXY#*://}" # user:pass@host:port
proxy_auth="${proxy_auth%@*}" # user:pass
proxy_port="${ALL_PROXY##*:}" # port

if command -v ncat >/dev/null 2>&1; then
cat >> "${CLAUDE_ENV_FILE}" <<ENVEOF
export GIT_SSH_COMMAND='ssh -o ControlMaster=no -o ControlPath=none -o '\''ProxyCommand=ncat --proxy-type socks5 --proxy-auth ${proxy_auth} --proxy 127.0.0.1:${proxy_port} %h %p'\'''
ENVEOF
echo "SessionStart:startup hook success: sandbox-ssh-fix: replaced broken BSD nc with ncat for SOCKS5 proxy auth"
else
echo "export GIT_SSH_COMMAND='ssh -o ControlMaster=no -o ControlPath=none'" >> "${CLAUDE_ENV_FILE}"
echo "SessionStart:startup hook success: sandbox-ssh-fix: ncat not found, bypassing SOCKS5 proxy (using direct SSH)"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fi
Loading