anthropics / anthropics/claude-code

Cloud sessions: GH_TOKEN set in environment variables is ignored for api.github.com; Dependabot alerts unreachable

オープン
#88,198 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:docs area:networking area:security bug platform:web
主要言語
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

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。