anthropics / anthropics/claude-code
Cloud sessions: GH_TOKEN set in environment variables is ignored for api.github.com; Dependabot alerts unreachable
- 主要言語
- Python
- スター
- 145k
- フォーク
- 23.1k
- PR マージ指標
- PR 指標を取得中
説明
## Summary
In an Anthropic-hosted cloud session, a `GH_TOKEN` / `GITHUB_TOKEN` set in the cloud environment's variables is present in the container but **has no effect on outbound `api.github.com` requests**. The GitHub proxy re-authenticates every REST call with the session's own credential, so `gh` runs as the Claude GitHub App user-to-server identity no matter what token is set.
The docs currently state the opposite, which is what led me down a long debugging path.
Two asks, separable:
1. **Docs correction** (small): make it explicit that a user-supplied token does not authenticate `api.github.com` requests.
2. **Capability gap** (the reason I set a token at all): security alerts are unreachable from cloud sessions. `dependabot/alerts` is rejected by GitHub because the proxy's credential lacks `dependabot_alerts: read`, and no user-supplied credential can substitute.
## Environment
- Claude Code `2.1.237`, Anthropic-hosted cloud environment (Trusted network access)
- `gh` `2.45.0`, installed at `/usr/bin/gh`
- Auth method: Claude GitHub App
- `GH_TOKEN` / `GITHUB_TOKEN` set in the environment's variables to a fine-grained PAT (`github_pat_…`, 93 chars). Not the `proxy-injected` placeholder — the values pass through to the container intact.
## Reproduction
With a fine-grained PAT set in the environment variables, replace it inline with a syntactically invalid value:
```console
$ GH_TOKEN=github_pat_TOTALLY_INVALID_0000 gh api user --jq '{login,type}'
{"login":"christophehurpeau","type":"User"}
```
An invalid token authenticates successfully. Response headers are identical for the real PAT and the garbage value:
```
Github-Authentication-Token-Expiration: 2026-08-20 16:46:12 UTC
X-Oauth-Scopes:
X-Accepted-Github-Permissions: allows_permissionless_access=true
```
Empty `X-Oauth-Scopes` plus a same-day expiry indicate a short-lived GitHub App user-to-server token, not the fine-grained PAT that was set.
A related symptom that makes this confusing to diagnose: `gh auth status` and `gh api` disagree, because the former validates over GraphQL (which the proxy pins to a fixed operation set) while the latter goes through the REST path.
```console
$ gh auth status
github.com
X Failed to log in to github.com using token (GH_TOKEN)
- The token in GH_TOKEN is invalid.
$ gh api user --jq .login
christophehurpeau
```
## Docs discrepancy
[Configure cloud environments § Work with GitHub issues and pull requests](https://code.claude.com/docs/en/cloud-environments) says:
> If you set a token, it passes through to the container unchanged, so your scripts, and GitHub's `gh` CLI if you install it, use it directly.
The first half is accurate — the environment variable arrives unchanged. The second half is not: `gh` does **not** use it against `api.github.com`. The § GitHub proxy section is closer, but scopes the caveat to GraphQL only:
> The restriction applies to every request through the proxy regardless of the credentials you supply, so a `GH_TOKEN` you set gets the same 403.
In practice credential substitution applies to REST as well. Suggested wording: a token you set is usable by scripts that talk to other hosts, but requests to `api.github.com` are re-authenticated by the proxy and a user-supplied token cannot widen (or narrow) what the session can reach.
Minor, same page: it states "GitHub's `gh` CLI isn't pre-installed" and gives setup-script install instructions, but `gh` 2.45.0 is present at `/usr/bin/gh` in this environment.
## Capability gap: security alerts
The three failure modes are distinguishable by the `documentation_url` in the error body, which is useful for triage:
| Layer | Signature | Endpoints observed |
| ------------------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------- |
| Proxy path allowlist | `documentation_url: docs.anthropic.com/…` | `secret-scanning/alerts`, `vulnerability-alerts`, `user/installations` |
| Proxy GraphQL pin | `This GraphQL query is not enabled for this session` | `gh pr list` and other GraphQL-backed commands |
| GitHub, using the proxy's credential | `documentation_url: docs.github.com/…` | `dependabot/alerts` |
So Dependabot alerts pass the proxy allowlist and are rejected by GitHub itself:
```console
$ gh api "repos/OWNER/REPO/dependabot/alerts?state=open"
{"message":"Resource not accessible by integration",
"documentation_url":"https://docs.github.com/rest/dependabot/alerts#list-dependabot-alerts-for-a-repository",
"status":"403"}
```
Identical response with the garbage token, confirming the user-supplied PAT is not a factor.
`GET /repos/{owner}/{repo}/dependabot/alerts` requires `dependabot_alerts: read` for App tokens, or `security_events` for OAuth/classic tokens ([community discussion #60612](https://github.com/orgs/community/discussions/60612)). The Claude GitHub App credential has neither, and because the proxy owns the `Authorization` header there is no supported way for a user to supply a credential that does.
Net effect: a cloud session cannot answer "what security alerts are open on this repo?" — a natural request for an agent that is otherwise well suited to triaging and fixing dependency advisories. The workaround is to leave the session and run `gh api` locally, or to infer the alert set indirectly from open Renovate/Dependabot PR titles and bodies, which misses any alert without an available fix.
## Requests
1. Correct the `GH_TOKEN` passthrough wording (and the `gh` pre-install note).
2. Add `dependabot_alerts: read` to the Claude GitHub App's requested repository permissions, so alerts are readable when an installation grants it.
3. Consider `secret_scanning_alerts: read` and adding `secret-scanning/alerts` to the proxy path allowlist, for the same reason. Lower priority — secret scanning results are more sensitive, so gating it differently is reasonable.
4. Optional, cheap, and would have saved most of this investigation: have the proxy surface a distinguishable response when it substitutes a credential the caller explicitly supplied, rather than silently succeeding as a different identity.
## Possibly related
- #70474 — user-supplied PAT overridden by the sandbox credential layer in routines (closed, not planned)
- #61189 — proxy OAuth credential lacks `workflow` scope, blocking `.github/workflows/*` writes
- #11078 — proxy strips `Authorization`, breaking GitHub Packages npm auth
- #10108 — egress proxy and GitHub subdomains
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
調査の方向性
Start with the cloud-environments documentation section and the GitHub proxy behavior described in the reproduction commands. Verify the REST endpoints and response signatures for api.github.com, Dependabot alerts, and secret-scanning alerts. Done means the token and gh wording is accurate and the requested App permissions and proxy allowlist changes have clear, reviewed decisions.
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- github
- 領域
- api, authentication, cloud, documentation, security
- issue の種類
- 機能追加
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 活発
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 48/100