Skip to content

fix(hook): drain stdin on early return, handle EPIPE in bridges - #17

Open
arr-wf wants to merge 3 commits into
mainfrom
fix/hook-drain-stdin-when-paused
Open

arr-wf wants to merge 3 commits into
mainfrom
fix/hook-drain-stdin-when-paused

Conversation

@arr-wf

@arr-wf arr-wf commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Summary

When ctxcop is paused (or an off switch is set), hook handlers return before reading stdin. A bridge that writes a payload larger than the pipe buffer (~64KB) gets EPIPE, and with no stdin error handler Node throws an unhandled error event that crashes the host. Seen in pi with a 400KB provider payload.

The drain now happens once in cmdHook, after the handler replies, so every harness is covered and new handlers cannot regress it. It is skipped when stdin is a terminal and for aider, which has no stdin hook. The pi and opencode bridges also get a stdin error handler, so an early-exiting hook fails open.

Affected handlers: all three pi handlers, plus 13 in the claudecode, codex, cursor and opencode adapters.

Test plan

  • go test -race -count=1 ./... passes (run with an isolated HOME, see notes)

  • gofmt -w . is clean

  • go vet ./... is clean

  • TestPausedHooksDrainStdin builds the binary, pauses via a temp HOME, and writes 400KB to a pipe for 15 hook events across all harnesses. Without the drain, 14 of 15 fail with broken pipe (Claude Code pre-tool-use reads stdin first).

  • TestBridgeSurvivesEarlyExitHook (pi and opencode) runs the shipped bridge in node against a fake hook that exits unread. Each fails with write EPIPE ... Unhandled 'error' event when its bridge fix is reverted.

  • Ran the built binary in a real terminal pane while paused: a terminal on stdin returns immediately, and 400KB piped in exits 0 and returns {}.

Security checklist

  • This PR does not change detection or bypass surface.
  • No new third-party Go dependency.
  • No new third-party GitHub Action.
  • No literal credential-shape strings in source outside fixture paths.
  • Audit-log entries: N/A. The change adds no block, redact, or warn decision path.

Reviewer notes

  • With the real HOME and ctxcop paused, the existing redaction tests in the harness packages fail because they read the live pause sentinel. They pass with an isolated HOME. Not fixed here; separate follow-up.
  • The node bridge tests need a Node that strips TypeScript types by default (v24). They skip when node is missing.
  • The Claude Code, Codex and Cursor apps spawn the hook themselves. This PR covers the hook binary side only.
  • Follow-up: integration tests for hook binaries and bridges in GitHub Actions.

🤖 Generated with Claude Code

arr-wf and others added 2 commits October 13, 2026 12:51
…dges

When ctxcop is paused (or an off switch is set), 13 handlers across the claudecode, codex, cursor and opencode adapters, and all three pi handlers, return before reading stdin. A bridge that writes a payload over the pipe buffer (~64KB) gets EPIPE, and with no stdin error handler Node throws an unhandled 'error' event that crashes the host (seen in pi).

Drain stdin once in cmdHook after the handler replies, so every harness is covered and new handlers cannot regress it. Skipped when stdin is a terminal and for aider, which has no stdin hook.

Add a stdin error handler to the pi and opencode bridges so an early-exiting hook fails open.

Tests: build the binary and write 400KB to every harness hook event while paused; run each shipped bridge in node against a fake hook that exits unread.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Build the binary with .exe and set USERPROFILE, which os.UserHomeDir reads on Windows. Refs SECOPS-1421.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

SECOPS-1421

SECOPS-1422

govulncheck flags net/http in 1.26.6 (HTTP/2 server crash, HPACK encoder race), fixed in 1.26.9. CI takes its Go version from go.mod. Also add Unreleased changelog entries for this PR.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant