refactor(guardrails): buffer_stdin stdin reads; blocking guards fail closed on timeout - #444
Conversation
…rds fail closed on timeout Replace the bare `INPUT=$(cat)` fd0 read in all seven guardrails entry hooks with the shared `hook::buffer_stdin` helper (bounded read, default 2s), so the Windows Win32-pipe late-EOF stall can no longer hang a hook — and with it every tool call — indefinitely. Timeout semantics are decided per hook class rather than copying the audit-hook `|| exit 0` collapse: - The five blocking guards (block-dangerous-git, block-hook-bypass, block-no-verify, secret-pattern-detection, hardcoded-path-check) FAIL CLOSED on rc 2: a stdin read that timed out before a complete payload means the guard could not evaluate the tool call, and skipping would pass exactly the traffic the guard exists to stop. They exit 2 with buffer_stdin's BLOCKED reason already on stderr. Empty stdin (rc 1) still skips, matching the previous empty-payload behavior. - The two advisory hooks (flag-commit-pr-skill-bypass, workflow-resilience-check) skip on any read failure, as before. guardrails 0.8.0. Epic #313 deferred-backlog item 3 (guardrails half; the 8 advisory plugins shipped in the sibling PR). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 4m 23s —— View job PR Review: refactor(guardrails): buffer_stdin stdin reads; blocking guards fail closed on timeout
Overall AssessmentThe core change is correct and well-reasoned: swapping bare Finding 1 — Medium:
|
…757) Closes #446 `hook::read_file_path` ran jq directly against the inherited fd0 with no read bound in `cli-flag-verify.sh` — the last remaining fd0-direct reader after the fleet-wide `hook::buffer_stdin` migration, same Windows Win32-pipe late-EOF stall class. Fix per the issue's decided shape: buffer first, pipe the payload into the parser; empty/timed-out stdin skips this advisory hook (plain `|| exit 0` collapse matching its class and its siblings). Guardrails 0.9.0 → 0.9.1 + CHANGELOG. Verification: `cli-flag-verify.test.sh` PASS=37 FAIL=0 (35 baseline + 2 new empty-stdin skip-contract assertions); shellcheck clean. The new test asserts the skip contract, not the stall itself — a here-string/`/dev/null` harness cannot reproduce the Win32 late-EOF stall; behavior-preservation evidence is the suite green before/after with every case now routed through the buffered read. ## Related - #443 / #444 (the `hook::buffer_stdin` migration this completes) - #547 (hook-precision umbrella; this member carries its skip-contract guard) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary
Second half of the #313 item-3 stdin migration (sibling: #443, the 8 advisory plugins). All seven guardrails entry hooks now read stdin via the shared
hook::buffer_stdinhelper (boundedread -t, default 2s) instead of a barecat, so the Windows Win32-pipe late-EOF stall can no longer hang a hook — and with it every tool call — indefinitely.The load-bearing decision — blocking guards fail closed on timeout.
hook::buffer_stdindistinguishes rc 1 (empty/incomplete stdin) from rc 2 (read timed out before a complete JSON payload). The audit-hook idiom collapses both to a skip; for a security guard that would fail OPEN — a timed-out read means the guard could not evaluate the command, and skipping would pass exactly the traffic it exists to stop (dangerous git, hook bypass,--no-verify, secrets, hardcoded paths). Instead:block-dangerous-git,block-hook-bypass,block-no-verify,secret-pattern-detection,hardcoded-path-check): rc 2 →exit 2(block;buffer_stdinalready printed theBLOCKED:reason to stderr); rc 1 →exit 0(skip, matching the previous empty-payload behavior — these guards already skipped on emptyCOMMAND/fields after the barecat).flag-commit-pr-skill-bypass,workflow-resilience-check): any read failure → skip, as before.guardrails 0.8.0 (behavior change on the timeout path; the previous behavior was an indefinite hang, not a skip).
Verification
--rcfile=.shellcheckrc) +shfmt -dclean on all 7 hooksRelated
Epic #313 (deferred-backlog item 3, guardrails half). Sibling PR #443. Also relates to #317, #323.
No linked issue: incremental epic work; closes nothing.
🤖 Generated with Claude Code