sandbox credentials.files mask produces no file at all for ~/.config/gh/hosts.yml

Status Open
Reported on v2.1.270
Maintainer reply None cached
Activity 0 comments · opened Sep 17, 2026

Sandbox credentials.files mask produces no file at all for ~/.config/gh/hosts.yml

Environment

  • Claude Code version: 2.1.270
  • Install method: native
  • Platform: Linux (remote/EC2-hosted session, launched via the Claude Code desktop app's remote client)
  • gh version: 2.100.0

Config (~/.claude/settings.json, relevant excerpt)

{
  "sandbox": {
    "enabled": true,
    "network": {
      "allowedDomains": ["github.com", "api.github.com", "..."],
      "strictAllowlist": true,
      "tlsTerminate": {}
    },
    "filesystem": {
      "denyRead": ["~/"],
      "allowRead": ["~/repos", "~/.gitconfig", "~/.config/git", "~/.config/gh/config.yml", "~/.cache/aws-bedrock", "~/.claude/plugins"]
    },
    "credentials": {
      "files": [
        {
          "path": "~/.config/gh/hosts.yml",
          "mode": "mask",
          "extract": "oauth_token:\\s*(\\S+)",
          "injectHosts": ["api.github.com"],
          "maskDuplicates": true,
          "onExtractNoMatch": "error"
        }
      ]
    }
  }
}

Expected behavior

Per docs (/docs/en/sandboxing#mask-credential-files): on Linux/WSL2, a sandboxed command reading ~/.config/gh/hosts.yml should see a sentinel copy (real token replaced with a placeholder), and outbound requests to api.github.com should get the real token substituted by the sandbox proxy.

Actual behavior

The file does not exist at all inside the sandbox — not a sentinel, not the real file, nothing:

$ cat ~/.config/gh/hosts.yml
cat: /home/ubuntu/.config/gh/hosts.yml: No such file or directory

$ ls -la ~/.config/gh/
total 4
drwxr-xr-x 2 ubuntu ubuntu   60 ... .
drwx------ 4 ubuntu ubuntu   80 ... ..
-rw------- 1 ubuntu ubuntu 1660 ... config.yml

hosts.yml doesn't appear in the directory listing at all — not even as a masked entry. As a consequence, gh auth status reports "not logged in" and git ls-remote/git push/git pull over HTTPS fail with could not read Username for 'https://github.com' inside the sandbox, even though the same commands work fine outside it.

Verification already done (all pass)

  1. extract regex matches: confirmed via grep -qE 'oauth_token:[[:space:]]*\S+' ~/.config/gh/hosts.yml outside the sandbox → matches (twice — hosts.yml has oauth_token: both nested under users.<login> and duplicated at the top level, which is normal for current gh versions).
  2. File validity: file ~/.config/gh/hosts.yml → ASCII text; wc -c → 216 bytes. Well under the documented 8 MiB cap, not a directory, not a glob.
  3. network.tlsTerminate: {} is set, and confirmed active empirically — HTTPS_PROXY/HTTP_PROXY/GIT_SSH_COMMAND inside the sandbox are all wired through the sandbox's TLS-terminating proxy (localhost:3128).
  4. api.github.com is present in both injectHosts and network.allowedDomains.
  5. Config schema matches the documented example in /docs/en/sandboxing#mask-credential-files exactly, plus two optional fields (maskDuplicates, onExtractNoMatch) valid since v2.1.224 (we're on 2.1.270).
  6. Settings symlink is live and correct: ~/.claude/settings.json → <project>/dotfiles/claude_1/.claude/settings.json, confirmed via readlink -f.
  7. Not a stale-session issue: reproduced in a session started fresh after the config was last edited.
  8. /sandbox → Config tab shows the base filesystem/network restrictions (~/.config/gh correctly shown denied except the explicit config.yml allowRead exception) but has no dedicated section reporting sandbox.credentials mask status, so it can't confirm/deny whether the mask ran. No warning banner appeared at session start. No Dependencies tab appeared (so the optional Linux seccomp package isn't the issue).

None of the documented fallback-to-deny conditions (directory path, glob pattern, file >8 MiB, non-UTF-8) apply, and onExtractNoMatch: "error" shouldn't trigger since the regex does match. Per docs, masking is supposed to be enforced independently of the filesystem layer's denyRead, so the broad "denyRead": ["~/"] shouldn't prevent the mask's sentinel from appearing — but empirically, it looks like it does (or something else silently drops this entry) for this file.

Suspected cause (unconfirmed)

Possibly specific to this environment: a remote/EC2-hosted session launched via the desktop app's remote client, rather than a local interactive claude CLI session. This box has at least one other documented instance of remote/EC2-launched sessions not getting credential-handling behavior that's otherwise expected (AWS credential env-var injection via awsCredentialExport doesn't reach the sandboxed process env in this same launch mode, per separate investigation). If file-mode credentials.files masking has a similar gap for this launch path, that would explain a silent, total absence rather than a reported misconfiguration.

Workaround in use

Route gh/git operations over HTTPS through the unsandboxed-command escalation path instead of relying on the mask.

View original on GitHub ↗