Desktop app: pass the session OAuth token to the CLI over the initialize channel (or an inherited fd), not `CLAUDE_CODE_OAUTH_TOKEN`; and stop exporting `CLAUDE_CODE_MESSAGING_TOKEN` into tool shells

Status Open
Reported on v2.1.285
Maintainer reply None cached
Activity 0 comments · opened Oct 10, 2026

Environment: Claude desktop app (macOS) launching Claude Code 2.1.285; macOS 26.6.2, arm64.

What happens. When the desktop app starts a Code session, CLAUDE_CODE_OAUTH_TOKEN is in the environment of both Claude.app/Contents/Helpers/disclaimer and the claude CLI it execs. On macOS any process of the same user can read another's exec-time environment with sysctl(KERN_PROCARGS2) (what ps -E uses). Claude Code correctly strips the token from Bash-tool environments, but an agent command that inspects its parent reads it anyway. On 2026-10-09 an agent's ps -wwE put the token into its transcript.

The Bash sandbox does not prevent this. Inside it, ps fails only because it is setuid. A short Python sysctl(KERN_PROCARGS2) call on the parent claude pid still returned its full environment (64 entries), token included (sandbox-runtime#160 tracks the sandbox side). On Linux, CLAUDE_CODE_SUBPROCESS_ENV_SCRUB already handles this case with a PID namespace; macOS has no equivalent.

Requests.

  1. Pass the token to the CLI in the stdin initialize message the desktop already sends (--input-format, --await-initialize), or on an inherited file descriptor, and keep it out of the environment of the helper and the CLI. OWASP's Secrets Management Cheat Sheet recommends against environment variables for secrets; CWE-214 describes this exposure.
  2. As defense in depth, sign claude and the desktop's helper with the restrict code-signing flag (codesign -o restrict, CS_RESTRICT). XNU's sysctl_procargsx() then withholds the environment from every other reader. I measured this on 26.6.2 with an ad-hoc-signed test binary: the environment block disappeared (4100 → 33 bytes), with arguments still readable.
  3. Don't export CLAUDE_CODE_MESSAGING_TOKEN to Bash and hooks by default on macOS/Linux, where the docs say the auth line is optional. Or export it only when a hook or command needs it. Today every agent shell carries a bearer token that any same-user process can read. (CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 removes it, but that is opt-in.)
  4. In the macOS sandbox profile, add (deny process-info* (target others)) after (allow process-info* (target same-sandbox)). On 26.6.2 that one line turned KERN_PROCARGS/KERN_PROCARGS2 reads of outside processes into EPERM; deny default alone does not. Details are on sandbox-runtime#160.

Related: anthropics/sandbox-runtime#160 (sandbox side, open); anthropics/claude-code#80045 (MCP secrets visible in argv from the VS Code launcher). I found no existing issue about the desktop app's environment token.

Drafted with AI assistance (Claude Code), from measurements run on my machine.

View original on GitHub ↗