CLAUDE_ENV_FILE contents leaked via ps aux, transcript, and accumulated session-env files

Resolved 💬 2 comments Opened Apr 3, 2026 by yonatan-genai Closed May 17, 2026

Summary

Environment variables written to CLAUDE_ENV_FILE by SessionStart hooks are leaked through three channels:

  1. Visible to all users via ps aux — contents are inlined into the bash -c argv, not sourced from a file
  2. Persisted in session transcript JSONL — the full bash -c command string including secrets is logged
  3. Accumulated in ~/.claude/session-env/ as world-readable files — one copy per session, never cleaned up

Reproduction

  1. Create a .env.local with a secret (e.g., GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxx)
  2. Add a SessionStart hook that loads it:

``json
{
"SessionStart": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "if [ -f \"$CLAUDE_PROJECT_DIR/.env.local\" ]; then grep -v '^#' \"$CLAUDE_PROJECT_DIR/.env.local\" | grep -v '^$' | sed 's/^/export /' >> \"$CLAUDE_ENV_FILE\"; fi"
}]
}]
}
``

  1. Run any bash command in CC
  2. In another terminal: ps aux | grep bash — the secret is in the command args
  3. Check the session JSONL — secrets embedded in bash tool_use entries

Root cause

The session environment file's contents are inlined directly into the bash -c command string rather than sourced from the file by path. So export SECRET=value becomes part of the argv visible to all users.

Suggested fix

Source the session-env file by path instead of inlining its contents:

# Current behavior:
bash -c 'source snapshot.sh && export SECRET=value && eval "user command"'

# Proposed:
bash -c 'source snapshot.sh && source /path/to/session-env.sh && eval "user command"'

This keeps the same behavior (env vars are available to the command) but:

  • ps aux shows source /path/to/file instead of the secret values
  • Transcript logs the source command, not the secrets
  • No behavior change for users

Additional issues

  • Session-env files are created with 644 permissions (world-readable). Should be 600.
  • Session-env directories are never cleaned up. Over time, secrets accumulate across hundreds of session directories.
  • The docs don't warn that CLAUDE_ENV_FILE is not safe for secrets. The official hook examples show non-secret usage (PROJECT_TYPE=nodejs) but issue #20528 noted the docs previously had export API_KEY=your-api-key as an example. Loading .env files is what env files are for — users will do this.

Impact

Any user who loads secrets via CLAUDE_ENV_FILE leaks them to:

  • All users on the machine (via ps aux)
  • Anyone with access to ~/.claude/ (transcript + session-env files)

Environment

  • Claude Code 2.1.91
  • macOS (Darwin 24.4.0)

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗