Summary
In the local-analysis (Claude Code engine) environment, the MCP gateway bearer token written to /tmp/gh-aw/mcp-config/mcp-servers.json (mode 0600) is shared between the github (read) and safeoutputs (write-sink) MCP servers. Because host.docker.internal is listed in NO_PROXY, direct HTTP calls to both MCP servers bypass the Squid proxy firewall entirely. Any subprocess running as the runner user can read this file, open an authenticated MCP session with the safeoutputs server, and invoke create_issue (and other write tools) without going through Claude Code's --allowed-tools enforcement. The full token is also logged in plaintext in /tmp/gh-aw/latest and /tmp/gh-aw/agent-stdio.log (tool-result replay), widening the exposure surface.
This is the local-analysis/Claude Code–specific instance of the same root cause tracked in #23740 (which was filed from the github-edit/Copilot engine finding). It needs verification that the upstream fix for #23740 covers the Claude engine config path and host.docker.internal+NO_PROXY gateway details.
Affected Area
Safe-outputs write-sink MCP caller-identity boundary — local-analysis profile, Claude Code engine. Config path: /tmp/gh-aw/mcp-config/mcp-servers.json (written by convert_gateway_config_claude.sh).
Reproduction Outline
- Run a
local-analysis-profile gh-aw workflow with the Claude Code engine and Bash tool access enabled.
- From a subprocess (as
runner), read the bearer token: TOKEN=$(python3 -c "import json; d=json.load(open('/tmp/gh-aw/mcp-config/mcp-servers.json')); print(list(d['mcpServers'].values())[0]['headers']['Authorization'])")
- Initialize an MCP session directly with the safeoutputs HTTP endpoint at
host.docker.internal (bypasses Squid via NO_PROXY): curl -X POST <safeoutputs-url> -H "Authorization: $TOKEN" -d '{"jsonrpc":"2.0","id":1,"method":"initialize",...}'
- Send
notifications/initialized with the returned Mcp-Session-Id.
- Call
tools/list — confirm create_issue is advertised and reachable.
- Call
create_issue directly, bypassing Claude Code's --allowed-tools list.
Observed Behavior
Steps 1–5 succeed: MCP session initializes, tools/list confirms create_issue is available on the safeoutputs server. The write tool is reachable by any runner-user process without any caller-identity check. The full bearer token also appears in plaintext in /tmp/gh-aw/latest (line 292) despite being redacted in Claude Code's own debug log.
Expected Behavior
The safeoutputs MCP server should reject calls from any caller other than the legitimate agent process — either by using a distinct token (not shared with the read-only github server), a process-bound secret, or a caller-identity enforcement mechanism at the gateway layer. Direct HTTP access to the write-sink should not be equivalent to agent-mediated access.
Security Relevance
A workflow's --allowed-tools restriction on Claude Code is the primary control preventing unsanctioned write-sink calls during execution. Bypassing it via direct MCP HTTP calls means any code or prompt-injected instruction that gains Bash access can enqueue arbitrary GitHub write operations (issue creation, comments, PR creation) regardless of what the workflow author configured. The NO_PROXY bypass also means this traffic is invisible to the egress firewall log.
Original finding: https://github.com/githubnext/gh-aw-security/issues/1695
gh-aw version: v0.67.1
Generated by File Issue · ● 404.1K · ◷
Summary
In the
local-analysis(Claude Code engine) environment, the MCP gateway bearer token written to/tmp/gh-aw/mcp-config/mcp-servers.json(mode 0600) is shared between thegithub(read) andsafeoutputs(write-sink) MCP servers. Becausehost.docker.internalis listed inNO_PROXY, direct HTTP calls to both MCP servers bypass the Squid proxy firewall entirely. Any subprocess running as therunneruser can read this file, open an authenticated MCP session with the safeoutputs server, and invokecreate_issue(and other write tools) without going through Claude Code's--allowed-toolsenforcement. The full token is also logged in plaintext in/tmp/gh-aw/latestand/tmp/gh-aw/agent-stdio.log(tool-result replay), widening the exposure surface.This is the
local-analysis/Claude Code–specific instance of the same root cause tracked in #23740 (which was filed from thegithub-edit/Copilot engine finding). It needs verification that the upstream fix for #23740 covers the Claude engine config path andhost.docker.internal+NO_PROXYgateway details.Affected Area
Safe-outputs write-sink MCP caller-identity boundary —
local-analysisprofile, Claude Code engine. Config path:/tmp/gh-aw/mcp-config/mcp-servers.json(written byconvert_gateway_config_claude.sh).Reproduction Outline
local-analysis-profile gh-aw workflow with the Claude Code engine andBashtool access enabled.runner), read the bearer token:TOKEN=$(python3 -c "import json; d=json.load(open('/tmp/gh-aw/mcp-config/mcp-servers.json')); print(list(d['mcpServers'].values())[0]['headers']['Authorization'])")host.docker.internal(bypasses Squid viaNO_PROXY):curl -X POST <safeoutputs-url> -H "Authorization: $TOKEN" -d '{"jsonrpc":"2.0","id":1,"method":"initialize",...}'notifications/initializedwith the returnedMcp-Session-Id.tools/list— confirmcreate_issueis advertised and reachable.create_issuedirectly, bypassing Claude Code's--allowed-toolslist.Observed Behavior
Steps 1–5 succeed: MCP session initializes,
tools/listconfirmscreate_issueis available on the safeoutputs server. The write tool is reachable by any runner-user process without any caller-identity check. The full bearer token also appears in plaintext in/tmp/gh-aw/latest(line 292) despite being redacted in Claude Code's own debug log.Expected Behavior
The safeoutputs MCP server should reject calls from any caller other than the legitimate agent process — either by using a distinct token (not shared with the read-only
githubserver), a process-bound secret, or a caller-identity enforcement mechanism at the gateway layer. Direct HTTP access to the write-sink should not be equivalent to agent-mediated access.Security Relevance
A workflow's
--allowed-toolsrestriction on Claude Code is the primary control preventing unsanctioned write-sink calls during execution. Bypassing it via direct MCP HTTP calls means any code or prompt-injected instruction that gainsBashaccess can enqueue arbitrary GitHub write operations (issue creation, comments, PR creation) regardless of what the workflow author configured. TheNO_PROXYbypass also means this traffic is invisible to the egress firewall log.Original finding: https://github.com/githubnext/gh-aw-security/issues/1695
gh-aw version: v0.67.1