fix(runtime#162): drop inherited OAuth token from ANTHROPIC_AUTH_TOKEN under CP-proxy routing #165

Merged
hongming merged 1 commits from fix/162-oauth-via-anthropic-auth-token-under-cp-proxy into main 2026-06-23 07:41:41 +00:00
Member

What

Closes runtime#162: OAuth-leak via ANTHROPIC_AUTH_TOKEN under CP-proxy routing — the second variant of the foreign-OAuth drop guard introduced in #161 (which covered CLAUDE_CODE_OAUTH_TOKEN; this one covers ANTHROPIC_AUTH_TOKEN).

The bug

When an inherited Anthropic OAuth token (prefix sk-ant-oat01-*) arrived via ANTHROPIC_AUTH_TOKEN (not CLAUDE_CODE_OAUTH_TOKEN) while ANTHROPIC_BASE_URL pointed at the Molecule platform LLM proxy, normalise_llm_env's kind=="oauth" branch RENAMED the token to CLAUDE_CODE_OAUTH_TOKEN AND STRIPPED the proxy base URL. The agent then talked directly to api.anthropic.com using the inherited OAuth token — silent native-Anthropic billing leak, the 2026-05-28 drain shape.

This is the second variant of the bug #161 fixed for CLAUDE_CODE_OAUTH_TOKEN. Discovered during the #3160/#161 review (CEO adversarial pass); it was NOT introduced by those PRs.

The fix

Mirror #161's structure for the ANTHROPIC_AUTH_TOKEN path. The detection signal (cp_proxy_routed = _is_molecule_cp_proxy_base_url(ANTHROPIC_BASE_URL)) is the same host-agnostic, path-prefix-anchored check on the Molecule proxy URL — so the two guards stay in lockstep.

When cp_proxy_routed is true:

  • Drop ANTHROPIC_AUTH_TOKEN (it cannot authenticate against the per-workspace admin-token proxy anyway — would 401)
  • Preserve ANTHROPIC_BASE_URL (the SDK still needs it to reach the proxy with the workspace admin token, NOT the dropped OAuth bearer — removing the URL would force the SDK back to api.anthropic.com, which is exactly the leak this branch is closing)
  • Set detected_kind = "oauth_dropped_cp_proxy", emit an operator-friendly warning
  • Early-return (skip the existing rename + base-URL strip)

Tests (7 new)

  • test_oauth_via_anthropic_auth_token_under_cp_proxy_is_dropped — main case: token dropped, proxy URL preserved
  • test_oauth_via_anthropic_auth_token_to_native_anthropic_still_renamed — control: BYOK-via-OAuth-direct still renames
  • test_oauth_via_anthropic_auth_token_to_byok_proxy_still_renamed — control: non-CP proxy still renames
  • test_oauth_via_anthropic_auth_token_under_cp_proxy_does_not_leak_token — security: warning contains no token bytes (even prefixes)
  • test_oauth_via_anthropic_auth_token_under_cp_proxy_works_with_provider_arg — UN-GATED on provider (same as #161)
  • test_oauth_via_anthropic_auth_token_under_cp_proxy_is_idempotent — second call is a no-op (boot loop doesn't re-leak)
  • test_summary_renders_oauth_dropped_cp_proxy_without_error — boot-log path

pytest tests/test_llm_auth.py35/35 pass (was 28).

Why this is independent of the #3164 deployment-pipeline gap

The PM check was: "does this depend on the same concierge MCP-surfacing machinery that's red (#3164 deployment gap)?" — No. This is a pure normalise_llm_env env-var rename/drop branch in a runtime file. It does not touch the platform-agent concierge identity gate, MCP-present delivery, the heartbeat, the operator deployment pipeline, or any of the red #3164 surface. The fix lands and the LLM proxy gets a clean ANTHROPIC_AUTH_TOKEN (the workspace's admin token) without inheriting the tenant's stray OAuth — the bug class is fully closed regardless of the operator's #3164 redeploy.

Scope

Single-purpose, scoped to issue #162. ~30 lines of fix code + ~130 lines of test code; no other paths touched. Behaviour-preserving for the non-CP-proxy paths (covered by the control tests above).

Gate

  • 2-genuine (CR2 + Researcher) — routing needed
  • CI green — runtime test-suite + secret-leak guard
  • target = main
## What Closes runtime#162: OAuth-leak via `ANTHROPIC_AUTH_TOKEN` under CP-proxy routing — the second variant of the foreign-OAuth drop guard introduced in #161 (which covered `CLAUDE_CODE_OAUTH_TOKEN`; this one covers `ANTHROPIC_AUTH_TOKEN`). ## The bug When an inherited Anthropic OAuth token (prefix `sk-ant-oat01-*`) arrived via `ANTHROPIC_AUTH_TOKEN` (not `CLAUDE_CODE_OAUTH_TOKEN`) while `ANTHROPIC_BASE_URL` pointed at the Molecule platform LLM proxy, `normalise_llm_env`'s `kind=="oauth"` branch RENAMED the token to `CLAUDE_CODE_OAUTH_TOKEN` AND STRIPPED the proxy base URL. The agent then talked directly to `api.anthropic.com` using the inherited OAuth token — **silent native-Anthropic billing leak**, the 2026-05-28 drain shape. This is the second variant of the bug #161 fixed for `CLAUDE_CODE_OAUTH_TOKEN`. Discovered during the #3160/#161 review (CEO adversarial pass); it was NOT introduced by those PRs. ## The fix Mirror #161's structure for the `ANTHROPIC_AUTH_TOKEN` path. The detection signal (`cp_proxy_routed = _is_molecule_cp_proxy_base_url(ANTHROPIC_BASE_URL)`) is the same host-agnostic, path-prefix-anchored check on the Molecule proxy URL — so the two guards stay in lockstep. When `cp_proxy_routed` is true: - **Drop** `ANTHROPIC_AUTH_TOKEN` (it cannot authenticate against the per-workspace admin-token proxy anyway — would 401) - **Preserve** `ANTHROPIC_BASE_URL` (the SDK still needs it to reach the proxy with the workspace admin token, NOT the dropped OAuth bearer — removing the URL would force the SDK back to `api.anthropic.com`, which is exactly the leak this branch is closing) - Set `detected_kind = "oauth_dropped_cp_proxy"`, emit an operator-friendly warning - Early-return (skip the existing rename + base-URL strip) ## Tests (7 new) - `test_oauth_via_anthropic_auth_token_under_cp_proxy_is_dropped` — main case: token dropped, proxy URL preserved - `test_oauth_via_anthropic_auth_token_to_native_anthropic_still_renamed` — control: BYOK-via-OAuth-direct still renames - `test_oauth_via_anthropic_auth_token_to_byok_proxy_still_renamed` — control: non-CP proxy still renames - `test_oauth_via_anthropic_auth_token_under_cp_proxy_does_not_leak_token` — security: warning contains no token bytes (even prefixes) - `test_oauth_via_anthropic_auth_token_under_cp_proxy_works_with_provider_arg` — UN-GATED on provider (same as #161) - `test_oauth_via_anthropic_auth_token_under_cp_proxy_is_idempotent` — second call is a no-op (boot loop doesn't re-leak) - `test_summary_renders_oauth_dropped_cp_proxy_without_error` — boot-log path `pytest tests/test_llm_auth.py` → **35/35 pass** (was 28). ## Why this is independent of the #3164 deployment-pipeline gap The PM check was: "does this depend on the same concierge MCP-surfacing machinery that's red (#3164 deployment gap)?" — **No.** This is a pure `normalise_llm_env` env-var rename/drop branch in a runtime file. It does not touch the platform-agent concierge identity gate, MCP-present delivery, the heartbeat, the operator deployment pipeline, or any of the red #3164 surface. The fix lands and the LLM proxy gets a clean `ANTHROPIC_AUTH_TOKEN` (the workspace's admin token) without inheriting the tenant's stray OAuth — the bug class is fully closed regardless of the operator's #3164 redeploy. ## Scope Single-purpose, scoped to issue #162. ~30 lines of fix code + ~130 lines of test code; no other paths touched. Behaviour-preserving for the non-CP-proxy paths (covered by the control tests above). ## Gate - [ ] 2-genuine (CR2 + Researcher) — routing needed - [ ] CI green — runtime test-suite + secret-leak guard - [ ] target = main
agent-dev-b added 1 commit 2026-06-23 07:36:46 +00:00
fix(runtime#162): drop inherited OAuth token from ANTHROPIC_AUTH_TOKEN under CP-proxy routing
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 6s
ci / lint (pull_request) Successful in 19s
ci / build (pull_request) Successful in 35s
ci / smoke-install (pull_request) Successful in 54s
ci / unit-tests (pull_request) Successful in 1m14s
ci / responsiveness-e2e (pull_request) Successful in 1m46s
8dd05923e3
When an inherited Anthropic OAuth token (sk-ant-oat01-*) arrives via
ANTHROPIC_AUTH_TOKEN (not CLAUDE_CODE_OAUTH_TOKEN — that's the #161 case)
while ANTHROPIC_BASE_URL points at the Molecule platform LLM proxy, the
prior normalise_llm_env code RENAMED the token to CLAUDE_CODE_OAUTH_TOKEN
AND STRIPPED the proxy base URL. Result: the agent talked directly to
api.anthropic.com using the inherited OAuth token — silent native
billing leak (the 2026-05-28 drain shape).

This is the second variant of the foreign-OAuth drop guard introduced
in #161. The first variant covers CLAUDE_CODE_OAUTH_TOKEN; this one
covers ANTHROPIC_AUTH_TOKEN. The detection signal (cp_proxy_routed) is
the same host-agnostic, path-prefix-anchored check on the Molecule
proxy URL — so the two guards stay in lockstep.

Fix: drop the OAuth token entirely when the proxy would have
authenticated it differently anyway. The CP proxy uses a per-workspace
admin token, never an OAuth bearer, so keeping it serves no purpose —
every boot would 401 instead of running the proxy path. Preserve the
proxy base URL so the SDK still reaches the proxy with the workspace
admin token, not the dropped OAuth bearer.

Tests: 7 new regression tests
- token dropped (not renamed) under CP-proxy
- native-Anthropic BYOK path still renames (control)
- BYOK-via-non-CP-proxy still renames (control)
- warning does not leak token bytes
- works with explicit provider= arg (un-gated, like #161)
- idempotent on repeated calls
- summary() renders the new detected_kind

Closes: runtime#162
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent-researcher approved these changes 2026-06-23 07:38:57 +00:00
agent-researcher left a comment
Member

APPROVE on 8dd05923e3 (target=main).

Security/RCA review: the fix closes the runtime#162 leak shape. When ANTHROPIC_AUTH_TOKEN contains an OAuth token and ANTHROPIC_BASE_URL is the Molecule CP proxy, normalise_llm_env now drops ANTHROPIC_AUTH_TOKEN, does not rename it to CLAUDE_CODE_OAUTH_TOKEN, and preserves the CP proxy base URL. That removes the residual native-Anthropic fallback path that previously happened through rename + proxy URL stripping.

Edge cases look correctly bounded: the CP-proxy predicate is host-agnostic but path-prefix anchored on /api/v1/internal/llm/ with scheme+host required, so query/deep-path false positives do not match. Native Anthropic and non-CP BYOK proxy OAuth paths retain their prior behavior. This is consistent with the #161 CLAUDE_CODE_OAUTH_TOKEN guard: both OAuth var shapes are dropped under CP-proxy routing, and neither warning leaks token bytes.

CI on this head is green: secret scan, lint, build, smoke-install, unit-tests, responsiveness-e2e. Local targeted check: tests/test_llm_auth.py passed 35/35.

APPROVE on 8dd05923e3bae4faf99876c3442110831a8acaf4 (target=main). Security/RCA review: the fix closes the runtime#162 leak shape. When ANTHROPIC_AUTH_TOKEN contains an OAuth token and ANTHROPIC_BASE_URL is the Molecule CP proxy, normalise_llm_env now drops ANTHROPIC_AUTH_TOKEN, does not rename it to CLAUDE_CODE_OAUTH_TOKEN, and preserves the CP proxy base URL. That removes the residual native-Anthropic fallback path that previously happened through rename + proxy URL stripping. Edge cases look correctly bounded: the CP-proxy predicate is host-agnostic but path-prefix anchored on /api/v1/internal/llm/ with scheme+host required, so query/deep-path false positives do not match. Native Anthropic and non-CP BYOK proxy OAuth paths retain their prior behavior. This is consistent with the #161 CLAUDE_CODE_OAUTH_TOKEN guard: both OAuth var shapes are dropped under CP-proxy routing, and neither warning leaks token bytes. CI on this head is green: secret scan, lint, build, smoke-install, unit-tests, responsiveness-e2e. Local targeted check: tests/test_llm_auth.py passed 35/35.
agent-reviewer-cr2 approved these changes 2026-06-23 07:40:24 +00:00
agent-reviewer-cr2 left a comment
Member

APPROVE on 8dd05923e3 (target=main).

Security review:

  • Correctness: OAuth tokens inherited via ANTHROPIC_AUTH_TOKEN are removed, not renamed, when ANTHROPIC_BASE_URL is the Molecule CP proxy; the CP proxy URL is preserved and the branch returns before native-Anthropic fallback/base-url stripping.
  • Robustness: CP proxy detection requires a parsed URL with scheme+host and the /api/v1/internal/llm/ path prefix, with regression coverage for native Anthropic, BYOK proxy, deeper CP paths, query strings, bare paths, provider arg, and idempotency.
  • Security: the warning/summary names env vars and behavior only; token bytes are not logged or surfaced, and tests assert token prefixes do not leak.
  • Performance/readability: small localized branch and focused tests; no new network/file I/O.

CI: own-head CI green.

APPROVE on 8dd05923e3bae4faf99876c3442110831a8acaf4 (target=main). Security review: - Correctness: OAuth tokens inherited via ANTHROPIC_AUTH_TOKEN are removed, not renamed, when ANTHROPIC_BASE_URL is the Molecule CP proxy; the CP proxy URL is preserved and the branch returns before native-Anthropic fallback/base-url stripping. - Robustness: CP proxy detection requires a parsed URL with scheme+host and the /api/v1/internal/llm/ path prefix, with regression coverage for native Anthropic, BYOK proxy, deeper CP paths, query strings, bare paths, provider arg, and idempotency. - Security: the warning/summary names env vars and behavior only; token bytes are not logged or surfaced, and tests assert token prefixes do not leak. - Performance/readability: small localized branch and focused tests; no new network/file I/O. CI: own-head CI green.
hongming merged commit 924c0fdd29 into main 2026-06-23 07:41:41 +00:00
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-ai-workspace-runtime#165