Summary
The MCP gateway bearer token — correctly protected at rest in /tmp/gh-aw/mcp-config/mcp-servers.json (mode 0600, directory mode 0700) — leaks in full into the world-readable Claude Code stdio log (/tmp/gh-aw/agent-stdio.log, mode 0644) when the agent reads that config file using the Read tool. The token appeared 8 times in the world-readable log: once in the tool_result record for the file read, and additionally in extended-thinking blocks where the model quoted the discovered token. The redact_secrets.cjs pipeline does not cover internally-minted MCP gateway tokens, so the leakage is unredacted.
Affected Area
Log-file access-control boundary. The tee -a /tmp/gh-aw/agent-stdio.log invocation in the parent shell runs with the default umask (022), producing a mode 0644 file readable by any process on the runner host — despite the config file being intentionally created at mode 0600 via umask 077 in convert_gateway_config_claude.sh. Additionally, redact_secrets.cjs covers only GitHub tokens, Azure/Google/AWS/Anthropic API keys, and OpenAI keys — not randomly-minted internal MCP gateway tokens (no recognizable fixed prefix).
Reproduction Outline
- Run a gh-aw workflow using the Claude Code engine (
local-analysis profile, claude-sonnet-4-6).
- Confirm permissions:
stat -c '%a %n' /tmp/gh-aw/mcp-config/mcp-servers.json → 600; stat -c '%a %n' /tmp/gh-aw/agent-stdio.log → 644.
- The agent discovers
/tmp/gh-aw/mcp-config/mcp-servers.json (e.g., via find /tmp -maxdepth 4 -type f) and reads it with the Read tool.
- The full file content — including the
Authorization bearer token for both github and safeoutputs MCP servers — is logged verbatim as a tool_result record in the stdio log.
- Extended-thinking blocks additionally quote the token as part of the model's reasoning, each producing another world-readable log entry.
- Verify:
grep -c 'Authorization' /tmp/gh-aw/agent-stdio.log → 8 (or more).
Observed Behavior
The MCP gateway token appears 8 times in /tmp/gh-aw/agent-stdio.log (mode 0644): once as the verbatim tool_result content of the Read call on mcp-servers.json, and multiple times in extended-thinking traces. Any process on the runner host that can read /tmp can recover the token without any file-system privilege.
Expected Behavior
The MCP gateway token must not appear in any world-readable log file. Either:
- The stdio log should be created at mode 0600 (e.g., set
umask 077 before tee, or pre-create the file at mode 0600 before the agent starts), and/or
redact_secrets.cjs (or an equivalent masking step) should register the MCP gateway Authorization token as a dynamic secret to be masked in all log sinks at workflow setup time.
Security Relevance
Any process running on the Docker bridge network or on the host that can read /tmp can recover the MCP gateway token without needing runner-user file access to the protected config directory. Since the same token is shared between the GitHub read MCP and the safeoutputs write-sink MCP (see also #1649), this leakage gives an attacker access to both read and write sinks. The finding contradicts two documented architecture claims simultaneously: (1) "secrets are passed via environment variables, never in configuration files"; (2) "Secret Redaction scans temporary files to prevent accidental credential leakage through logs, outputs, or artifacts."
Confirmed in v0.65.6.
Original finding: https://github.com/githubnext/gh-aw-security/issues/1697
Generated by File Issue · ● 231.3K · ◷
Summary
The MCP gateway bearer token — correctly protected at rest in
/tmp/gh-aw/mcp-config/mcp-servers.json(mode 0600, directory mode 0700) — leaks in full into the world-readable Claude Code stdio log (/tmp/gh-aw/agent-stdio.log, mode 0644) when the agent reads that config file using theReadtool. The token appeared 8 times in the world-readable log: once in thetool_resultrecord for the file read, and additionally in extended-thinking blocks where the model quoted the discovered token. Theredact_secrets.cjspipeline does not cover internally-minted MCP gateway tokens, so the leakage is unredacted.Affected Area
Log-file access-control boundary. The
tee -a /tmp/gh-aw/agent-stdio.loginvocation in the parent shell runs with the default umask (022), producing a mode 0644 file readable by any process on the runner host — despite the config file being intentionally created at mode 0600 viaumask 077inconvert_gateway_config_claude.sh. Additionally,redact_secrets.cjscovers only GitHub tokens, Azure/Google/AWS/Anthropic API keys, and OpenAI keys — not randomly-minted internal MCP gateway tokens (no recognizable fixed prefix).Reproduction Outline
local-analysisprofile, claude-sonnet-4-6).stat -c '%a %n' /tmp/gh-aw/mcp-config/mcp-servers.json→600;stat -c '%a %n' /tmp/gh-aw/agent-stdio.log→644./tmp/gh-aw/mcp-config/mcp-servers.json(e.g., viafind /tmp -maxdepth 4 -type f) and reads it with theReadtool.Authorizationbearer token for bothgithubandsafeoutputsMCP servers — is logged verbatim as atool_resultrecord in the stdio log.grep -c 'Authorization' /tmp/gh-aw/agent-stdio.log→ 8 (or more).Observed Behavior
The MCP gateway token appears 8 times in
/tmp/gh-aw/agent-stdio.log(mode 0644): once as the verbatimtool_resultcontent of theReadcall onmcp-servers.json, and multiple times in extended-thinking traces. Any process on the runner host that can read/tmpcan recover the token without any file-system privilege.Expected Behavior
The MCP gateway token must not appear in any world-readable log file. Either:
umask 077beforetee, or pre-create the file at mode 0600 before the agent starts), and/orredact_secrets.cjs(or an equivalent masking step) should register the MCP gatewayAuthorizationtoken as a dynamic secret to be masked in all log sinks at workflow setup time.Security Relevance
Any process running on the Docker bridge network or on the host that can read
/tmpcan recover the MCP gateway token without needingrunner-user file access to the protected config directory. Since the same token is shared between the GitHub read MCP and the safeoutputs write-sink MCP (see also #1649), this leakage gives an attacker access to both read and write sinks. The finding contradicts two documented architecture claims simultaneously: (1) "secrets are passed via environment variables, never in configuration files"; (2) "Secret Redaction scans temporary files to prevent accidental credential leakage through logs, outputs, or artifacts."Confirmed in v0.65.6.
Original finding: https://github.com/githubnext/gh-aw-security/issues/1697