[FEATURE] sandbox credentials mask: substitute the sentinel inside HTTP Basic auth (Base64) so git over HTTPS can authenticate
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
I run Claude Code in a dev container with the Bash sandbox enabled, and I mask a GitHub PAT with sandbox.credentials.files (~/.config/gh/hosts.yml, mode: mask, injectHosts: ["api.github.com", "github.com"], network.tlsTerminate: {}) so that sandboxed commands never hold the real token.
This works for gh (gh api user succeeds through the proxy), but it does not work for git over HTTPS. git cannot clone, fetch or push a private repository inside the sandbox. This leaves me without a way to give sandboxed git access to GitHub while keeping the token hidden from the commands Claude runs.
The cause is that HTTP Basic auth Base64-encodes the credential. git (through any credential helper, including gh auth git-credential) sends Authorization: Basic base64(user:password), and the password is the sentinel. The docs say substitution covers headers and request bodies, so the proxy looks for the sentinel as literal text. The sentinel is only present inside the Base64 string, so it is not found, and the sentinel reaches GitHub unchanged.
Evidence, all inside the sandbox, with the same masked token and the same injectHosts entry:
$ gh api user --jq .login # token sent as a header: works
<my-login>
$ curl -s -o /dev/null -w '%{http_code}\n' -u "<my-login>:$(gh auth token)" https://api.github.com/user
401 # same token sent as Basic auth: fails
$ GIT_TERMINAL_PROMPT=0 git ls-remote https://github.com/<owner>/<private-repo>.git HEAD
remote: Invalid username or token. Password authentication is not supported for Git operations.
fatal: Authentication failed for 'https://github.com/<owner>/<private-repo>.git/'
A trace with GIT_TRACE_CURL=1 GIT_TRACE_REDACT=0 shows git sending Authorization: Basic eXVr.... Decoding that value gives <my-login>:fake_value_..., so the sentinel is there, but only inside the encoded string.
I ruled out the other causes: the failure is a GitHub 401 and not a TLS error, so tlsTerminate and the CA are working; github.com is in both allowedDomains and injectHosts; and I restarted the session after adding it.
Proposed Solution
When a request goes to a host in injectHosts, the proxy should also handle Authorization: Basic <base64> headers:
- Decode the Base64 value.
- If the decoded
user:passwordcontains a sentinel from amaskentry, replace it with the real value. - Re-encode the value and forward the request.
Behavior I would expect around that:
- No new settings. The existing
maskentries andinjectHostsapply as they do today, so a setup that already works forghwould start working forgitwithout changes. - A Basic header that has no sentinel, or a value that is not valid Base64, is forwarded unchanged.
- The real value is substituted only on connections that the domain allowlist admits and only for hosts in
injectHosts, exactly as for header-based substitution today. The sandboxed command still only ever sees the sentinel. - The docs for masking list Basic auth as supported, or state the limitation until it is.
This is comparable to how the proxy already re-signs AWS SigV4 requests after substituting the values: it understands an encoding that carries the credential, so that a tool that authenticates in that form keeps working.
Alternative Solutions
What I tried or considered:
gh auth setup-git: registersgh auth git-credentialas the helper, and it runs inside the sandbox. It returns the sentinel, and git sends it in a Basic header, which fails as shown above.- Bearer /
tokenheader throughhttp.extraHeader:git -c http.extraHeader="Authorization: Bearer <sentinel>"andAuthorization: token <sentinel>both return401on the git endpoint. I could not tell whether GitHub rejects those schemes for git or whether the substitution did not happen, so this gives no positive control. sandbox.excludedCommandsforgit push/git clone: works, but the command then runs with no sandbox and can read the real token. Options such as--receive-packand repo-local hooks or config can also run code outside the sandbox, so this undoes the protection I wanted from masking.- Making the real token readable in the sandbox (for example through a credential helper that reads a secret file): works, but it defeats masking.
- Running clone and push on the host: works, but I lose the ability to let Claude push branches from the container.
Priority
Medium - Would be very helpful
Feature Category
Configuration and settings
Use Case Example
- I keep a fine-grained PAT (scoped to my repositories, Contents read/write) in
~/.config/gh/hosts.yml, on a tmpfs inside the container. - Managed settings mask that file, with
injectHosts: ["api.github.com", "github.com"]andnetwork.tlsTerminate: {}, so Claude's sandboxed commands only see a sentinel. - I ask Claude to clone a private repository and open a branch with a fix.
gh pr createandgh apicalls work, because the token goes out in a header.git clone https://github.com/<owner>/<private-repo>.gitfails withAuthentication failed, because git sends the token in a Base64-encoded Basic header.- With this feature, step 5 succeeds without any change to the settings, and the token is never visible to the commands Claude runs.
Additional Context
Environment
- Claude Code 2.1.267
- Linux, Docker dev container (kernel
Linux 7.0.12-linuxkit),sandbox.enableWeakerNestedSandbox: true gh2.101.0,git2.47.3
Configuration (excerpt)
{
"sandbox": {
"enabled": true,
"network": {
"tlsTerminate": {},
"allowedDomains": ["api.github.com", "github.com"]
},
"credentials": {
"files": [
{
"path": "~/.config/gh/hosts.yml",
"mode": "mask",
"extract": "oauth_token:\\s*(\\S+)",
"injectHosts": ["api.github.com", "github.com"]
}
]
}
}
}
Technical considerations
- The current behavior matches the docs ("Substitution covers headers and request bodies"), but the docs do not say that Basic auth is out of scope. The masking section uses
~/.config/gh/hosts.ymland.netrcas examples, and consumers of both, such asgitand curl with.netrc, send Basic auth. - Other tools that send a credential as Basic auth, such as npm
_authand Docker registryauth, are likely affected in the same way. I have only tested git and curl. - There is no startup warning and
claude doctordoes not report it, so the failure looks like a wrong token.
Related issues
- #77084 asks for broker-style injection ("agent can use secrets, never read them"). It does not mention Basic auth.
- #95135 reports
hosts.ymlmissing from the sandbox whendenyReadcovers~/.config/gh/. That is a separate problem: in my setuphosts.ymlis visible and holds a sentinel.