Skip to content

Claude engine safeoutputs MCP shared bearer token enables direct write-sink bypass in local-analysis environment #25102

@szabta89

Description

@szabta89

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

  1. Run a local-analysis-profile gh-aw workflow with the Claude Code engine and Bash tool access enabled.
  2. 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'])")
  3. 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",...}'
  4. Send notifications/initialized with the returned Mcp-Session-Id.
  5. Call tools/list — confirm create_issue is advertised and reachable.
  6. 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 ·

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions