[BUG] CLI binary spawns subprocesses (e.g. gh) outside the permission model — invisible to allowlist audits

Resolved 💬 3 comments Opened Apr 26, 2026 by bidossessi Closed May 28, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The Claude Code CLI binary executes external commands on the user's machine that are not routed through the Bash tool, not subject to permissions.allow / permissions.deny, and not surfaced in the permission UI. The PR-status polling that calls gh pr view --json … (#37583, #23580) is one example. There is no documented inventory of which subprocesses the binary may spawn on its own behalf, no permission surface that can opt out of them as a class, and no audit log to review.

This contradicts the documented permission model: file reads, file writes, Bash commands, MCP tools, and the WebFetch domain check are all gated by allowlist entries the user can inspect. A user auditing permissions.allow has no signal that any other process can be launched by the binary.

The specific gh polling has a per-feature workaround (prStatusFooterEnabled: false in ~/.claude.json), but per-feature opt-outs do not address the principle. Any future feature spawning git, kubectl, docker, op, or any other tool would ship with the same invisibility unless the design changes.

What Should Happen?

In increasing order of effort:

  1. Document the inventory. A docs page enumerating every external command the CLI may spawn outside the Bash tool surface, with trigger, cadence, data sent, and disable mechanism per command.
  2. Surface them in permissions.allow as synthetic entries (e.g. Internal(gh:pr-status)) following the same allow/deny/ask semantics as everything else.
  3. A single global toggle (e.g. internalSubprocessesEnabled: false) for users opting out of the entire class.

(2) is the closest fit with the existing permission model.

Error Messages/Logs

# execsnoop-bpfcc capture demonstrating spawn-from-claude:
PCOMM   PID     PPID    RET ARGS
gh      2591263 2515429 0   /usr/bin/gh pr view --json number,url,reviewDecision,isDraft,headRefName,state
gh      2591620 2591425 0   /usr/bin/gh auth token

$ ps -p 2515429 -o pid,ppid,comm,args
    PID    PPID COMMAND  COMMAND
2515429 1891746 claude   claude

permissions.allow at the time of capture: zero gh-related entries.

Steps to Reproduce

  1. Examine ~/.claude/settings.json and project / local — confirm no entries authorizing the binary to invoke gh
  2. Open Claude Code on a branch with an open PR
  3. In another shell: sudo execsnoop-bpfcc -n gh
  4. Observe gh invocations whose PPID resolves to the claude process
  5. Confirm: no permission prompt, no entry in permissions.allow, no documented authorization

Claude Model

Opus

Is this a regression?

I don't know

Claude Code Version

2.1.119 (Claude Code)

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Other

Additional Information

Terminal: ddterm (GNOME shell extension) running zsh on Ubuntu 24.04.4.

  • Related: #37583 (gh polling resource impact, has the prStatusFooterEnabled workaround), #23580 (closed stale; original reverse-engineering). The broader pattern of permission-bypass issues #16180 / #50046 / #50303 / #51902 covers user-invoked Tool calls escaping enforcement; this issue is the inverse — binary-invoked subprocesses that never enter the Tool layer.
  • prUrlTemplate and prStatusFooterEnabled already accept that this surface needs configuration. The request is to generalize that to any internal subprocess.

View original on GitHub ↗

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