feat(gh): allow gh api to POST PR review comments (unprompted)#43
Conversation
Makes the gh api write policy endpoint-aware: refuseGhApiWrite(args) becomes refuseGhApiCall(endpoint, args). GET stays allowed on the allowlist; POST is allowed (no prompt) only on the PR review-comment endpoints (repos/:o/:r/pulls/:n/comments and .../comments/:id/replies). PATCH/PUT/DELETE and --input (stdin/file body can't cross the relay) stay refused. Relax the in-box gh-shim handle_api to forward field/method flags (relay is the security boundary) while still rejecting --input locally. Both relay dispatch paths (docker + cloud) updated.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0a7a979. Configure here.
| arg.startsWith('--raw-field=') | ||
| ) { | ||
| return refuse(`field flag '${arg}' makes the request mutating`); | ||
| hasFieldFlag = true; |
There was a problem hiding this comment.
Parser doesn't consume field-flag values, enabling method misidentification
Medium Severity
refuseGhApiCall skips the value token for -X/--method (via i++) but not for -f, -F, --field, --raw-field, or other value-taking flags. When gh's pflag parses -f -X=GET, it consumes -X=GET as -f's value and auto-POSTs. But the relay sees -X=GET as a standalone method flag, concluding the method is GET. This mismatch lets a direct agentbox-ctl call trick the relay into treating a POST as a GET, bypassing the write-allowed endpoint check. Currently all allowlisted endpoints permit POST so it's not exploitable, but the code architecture explicitly anticipates future read-only endpoints where this would become a real bypass.
Reviewed by Cursor Bugbot for commit 0a7a979. Configure here.
…wngrade POST to GET Bugbot: refuseGhApiCall consumed the value of -X/--method but not of the spaced field flags (-f/-F/--field/--raw-field). pflag binds e.g. `-f -X=GET`'s `-X=GET` as the field value and POSTs, but the parser misread it as `--method GET` and classified the call as a read — a write-policy bypass once a read-only endpoint exists. Consume the value token for spaced field flags.


Summary
Extends the allowlisted
gh apiproxy (PR #39, read-only) so agents can post inline PR review comments — the one thinggh pr commentcan't do (it only posts conversation comments). Per request, comments are low-risk so this runs without a host confirm prompt.The write policy becomes endpoint-aware:
refuseGhApiWrite(args)→refuseGhApiCall(endpoint, args).repos/:o/:r/pulls/:n/commentsand.../comments/:id/replies.--input— refused: the hostghruns with stdin ignored and a box-side file path wouldn't exist on the host, so it can't cross the relay. Agents pass fields via-f/-F.Read vs write allowlists are separate consts (
GH_API_ALLOWED_ENDPOINTS⊇GH_API_WRITE_ALLOWED_ENDPOINTS) so a future read-only endpoint can be added without widening the POST surface.Layers
packages/relay/src/gh.ts— newGH_API_WRITE_ALLOWED_ENDPOINTS+isWriteAllowedGhApiEndpoint;refuseGhApiCall(reuses the existing robust pflag-spelling parsing to classify the effective method).packages/relay/src/{server,host-actions}.ts— both dispatch handlers callrefuseGhApiCall(endpoint, args); no prompt.packages/sandbox-docker/scripts/gh-shim—handle_apiforwards field/method flags (the relay is the security boundary), still rejects--inputlocally, expandedstrict_flagsallowlist.packages/ctl/src/commands/gh.ts— description wording.Notes
gh pr commentprompts;gh api … pulls/:n/comments(POST) does not — unprompted was the explicit choice forgh apicomments.Test plan
pnpm buildgreen; lint clean.refuseGhApiCall+isWriteAllowedGhApiEndpointcases), ctl 184/184 (shim now forwards POST/field flags, still rejects--input). (One unrelatedqueue.test.tstiming flake under parallel load; passes in isolation.)gh api repos/<o>/<r>/pulls/<n>/comments -f body=… -f commit_id=… -f path=… -F line=…posts an inline review comment;-X DELETE/issues/:n/commentsPOST /--input -each refused. Not yet run.🤖 Generated with Claude Code
Note
Medium Risk
Agents can create GitHub PR review comments without a host prompt on a narrow endpoint allowlist; relay-side parsing is the security boundary against bypass via glued flags or direct ctl calls.
Overview
Extends the allowlisted
gh apiproxy so agents can POST inline PR review comments (and replies) via GitHub’s REST API—somethinggh pr commentcannot do—while keeping the host as the only place that holds credentials.Policy moves from read-only argv checks (
refuseGhApiWrite) to endpoint-awarerefuseGhApiCall: GET on any proxied path; POST only onrepos/.../pulls/:n/commentsand.../comments/:id/replies(splitGH_API_WRITE_ALLOWED_ENDPOINTSfrom the overall allowlist so future read-only routes don’t widen writes); PATCH/PUT/DELETE refused;--inputrefused (bodies must use-f/-F). Comment POSTs run without a host confirm prompt (reads stay silent too).Layers: relay (
gh.ts, dockerserver.ts, cloudhost-actions.ts, exports); boxgh-shimforwards method/field flags and only blocks--inputlocally; ctl help text and tests updated.Reviewed by Cursor Bugbot for commit 0a7a979. Configure here.