Skip to content

Plan mode blocks read-only  gh api  GET/GraphQL queries (and read commands piped to python/jq) as "may modify the workspace" #4220

Description

@grantborthwick

Describe the bug

In plan mode, the command gate that is supposed to permit read-only investigation and block workspace mutations produces false positives for several read-only invocations of the gh CLI. Specifically, gh api <path> (an HTTP GET) and gh api graphql -f query='query{...}' (a read-only GraphQL query) are both blocked with "This shell command may modify the workspace and was blocked. Plan mode does not permit changes to the workspace." Separately, an otherwise-allowed read command (gh pr view ... --json ...) becomes blocked the moment it is piped into python (e.g. | python -c ...), even though the pipeline has no side effects.

The classifier appears to key on the surface tokens (gh api, and "pipes into an interpreter") rather than on the actual HTTP verb / mutation potential. This is a CLI-side command-classification issue, not a shell, OS, or antivirus problem: the blocked commands are pure reads with no filesystem or repo side effects.

Impact: plan mode's whole purpose is safe read-only investigation before proposing a plan. Inline pull-request review-thread comments are only retrievable via gh api / gh api graphql; blocking those forces the user either to abandon plan mode or to fall back to dumping raw JSON to a temp file and re-reading it with a file viewer, which still cannot reach inline review comments at all.

Affected version

GitHub Copilot CLI 1.0.72-1

Steps to reproduce the behavior

  1. Enter plan mode (Shift+Tab).
  2. Run gh pr view <prId> --repo <repo> --json state,mergeStateStatus -> it runs and returns JSON (correctly allowed as read-only).
  3. Run gh api repos/<repo>/pulls/<prId>/comments -> blocked with "This shell command may modify the workspace and was blocked. Plan mode does not permit changes to the workspace." This is a plain HTTP GET.
  4. Run gh api graphql -f query='query($o:String!){repository(owner:$o,name:"x"){id}}' -F o=bic -> blocked with the same message. This is a read-only GraphQL query (no mutation).
  5. Run gh pr view <prId> --repo <repo> --json comments | python -c "import sys;print(len(sys.stdin.read()))" -> blocked, even though step 2 shows gh pr view alone is allowed; the pipe into python is what flips it to blocked.

Example with internal repo/pr can be specified if needed.

Expected behavior

In plan mode the command gate must permit read-only commands. A bare gh api <path> GET and a gh api graphql request whose document is a query (not a mutation) do not modify the workspace and should be allowed. Piping an allowed read command into an interpreter (python, node, jq) purely to format or count output should not, by itself, cause a block; a pipeline should be judged by the mutation potential of its stages, not blanket-blocked because one stage is an interpreter reading stdin.

Additional context

  • Logs: can attach copilot debug-logs --session ffe57d43-4a60-40c9-b1a9-99614e7b5a2d on request.
  • Operating system: Windows (Windows_NT), PowerShell 5.x.
  • CPU architecture: x86_64.
  • Terminal emulator: Windows Terminal preview.
  • Shell: PowerShell (powershell.exe).
  • Ownership: this is the CLI's plan-mode command classifier, not the terminal/OS. The blocked commands are read-only HTTP requests with no side effects; the same session shows a sibling read command (gh pr view --json) is allowed, so the gate clearly has the notion of read-only but is mis-bucketing gh api and interpreter pipes.
  • Proposed fix (approx): in the plan-mode command classifier, do not treat gh api as mutating unless it carries a write verb: --method POST|PATCH|PUT|DELETE / -X <verb>, field-write flags (-f/--raw-field/-F/--field) on a non-graphql call, or a graphql document whose top-level operation is mutation. Treat bare gh api <path> and gh api graphql -f query='query ...' as read. For pipelines, classify by the union of stage effects rather than blocking any pipe whose downstream stage is python/node/jq reading stdin.
  • Regression guard: add plan-mode classifier tests asserting gh api <path> (GET) and gh api graphql with a query document are ALLOWED, while gh api -X POST ... and gh api graphql with a mutation document are BLOCKED; and that <allowed-read> | python -c ... remains allowed.
  • Workaround used this session: gh pr view 56494 --json comments,reviews,statusCheckRollup (allowed) dumped to stdout/temp file and read with the file viewer; but this exposes only the PR-level review body, NOT the inline review-thread comment, which requires the blocked gh api/gh api graphql.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:permissionsTool approval, security boundaries, sandbox mode, and directory restrictions

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions