diff --git a/tests/secret_guard_test.sh b/tests/secret_guard_test.sh index e6715ae..7e4966f 100644 --- a/tests/secret_guard_test.sh +++ b/tests/secret_guard_test.sh @@ -24,7 +24,7 @@ decision_for() { _tool=${2:-Bash} jq -n --arg t "$_tool" --arg c "$_cmd" \ '{tool_name: $t, tool_input: {command: $c}}' \ - | sh "$GUARD" \ + | "$GUARD" \ | jq -r '.hookSpecificOutput.permissionDecision // "MISSING"' } @@ -42,6 +42,20 @@ expect() { fi } +# Invoked as a PROGRAM above, never as `sh "$GUARD"`. Claude Code executes the +# hook directly, so its `#!/usr/bin/env bash` shebang decides the interpreter; +# piping it into `sh` overrides that and tests something production never runs. +# +# It is not academic. On Debian /bin/sh is dash, which has no `set -o +# pipefail`, so the guard died on line 23 before reading a byte of input and +# every deny case returned empty -- 20 of 20 assertions failed in CI while +# passing on a host where /bin/sh happens to be bash. A security gate that +# tests green and cannot start is the worst of both. +if [ ! -x "$GUARD" ]; then + printf 'FAIL: %s is not executable -- it must run via its own shebang\n' "$GUARD" + exit 1 +fi + if [ ! -f "$GUARD" ]; then printf 'FAIL: %s does not exist\n' "$GUARD" exit 1