Fail closed when the pre-bash guard cannot parse the payload#35
Merged
Conversation
Summary
make `_hook_parse_command` refuse to run a command it cannot inspect,
instead of silently allowing it.
Previously, after reading a payload, an empty extracted command exited 0
(allow) — so a missing/broken `jq` or malformed JSON disabled the entire
bash guard: the agent harness parses the payload with its own reader and
still runs `.tool_input.command`, so the command executed uninspected. A
single missing `jq` on PATH silently turned the guard off.
- for a PRE-execution hook (pre-bash), a present-but-unparsable payload
now fails CLOSED: `jq` missing, or `jq empty` failing (broken jq /
malformed JSON), blocks with an actionable message. `_hook_finish` for
this no-context hook emits `{}` + exit 2 without needing jq, so the
block works even when jq is what's missing — for both Claude and Codex
- genuinely empty stdin (no tool call) still exits 0 (no-op), and valid
JSON with no command field still allows (nothing to run; the harness
sees the same empty command)
- PostToolUse (post-bash) keeps its advisory no-op on parse failure — it
can't prevent an already-run command, so it must not emit a block
- suppress the raw jq error on the command-extraction line for
valid-but-non-object JSON
Testing
- new agent-hook-bash-parse-test: 21 assertions — pre-bash blocks on
malformed JSON and missing jq (Claude and Codex), allows empty stdin /
no-command / non-object JSON without stderr noise, and post-bash stays
a no-op on parse failure
- full suite: 1040 passed, 0 failed; checkrun lint + shellcheck -x clean
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Make
_hook_parse_commandrefuse to run a command it cannot inspect,instead of silently allowing it.
Previously, after reading a payload, an empty extracted command exited 0
(allow) — so a missing/broken
jqor malformed JSON disabled the entirebash guard: the agent harness parses the payload with its own reader and
still runs
.tool_input.command, so the command executed uninspected. Asingle missing
jqon PATH silently turned the guard off.now fails CLOSED:
jqmissing, orjq emptyfailing (broken jq /malformed JSON), blocks with an actionable message.
_hook_finishforthis no-context hook emits
{}+ exit 2 without needing jq, so theblock works even when jq is what's missing — for both Claude and Codex
JSON with no command field still allows (nothing to run; the harness
sees the same empty command)
can't prevent an already-run command, so it must not emit a block
valid-but-non-object JSON
Testing
agent-hook-bash-parse-test: 21 assertions — pre-bash blocks onmalformed JSON and missing jq (Claude and Codex), allows empty stdin /
no-command / non-object JSON without stderr noise, and post-bash stays
a no-op on parse failure
checkrun lint+shellcheck -xcleanReviewed by an adversarial fresh-eyes pass (fail-closed correctness for
both agents incl. missing-jq, no new bypass, no over-blocking); its three
low-severity findings — post-bash no-op, no stderr noise, Codex coverage —
are all folded into this change.