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
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.
- 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. - As defense in depth, sign
claudeand the desktop's helper with therestrictcode-signing flag (codesign -o restrict,CS_RESTRICT). XNU'ssysctl_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. - Don't export
CLAUDE_CODE_MESSAGING_TOKENto 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=1removes it, but that is opt-in.) - 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 turnedKERN_PROCARGS/KERN_PROCARGS2reads of outside processes intoEPERM;deny defaultalone 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.