From 5e19419981719e1048bfe7a4b03f102b14b50b43 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 6 Jul 2026 19:32:02 +0200 Subject: [PATCH] job4: SPEC-06 config-protection-payload-matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New T18-T20 in lib/tests/config-protection.test.sh (+4 assertions, 20→24). T18 Write payload, T19 MultiEdit payload → both exit 2 (pass trivially today — the extraction is tool-name-agnostic — but lock against a future narrowing to Edit-only; stated honestly, per report). T20 sentinel containing ONLY whitespace bytes (" \n\t", not literally empty) → exit 2 AND consumed — exercises config-protection.sh:44-46's `grep -q '[^[:space:]]'` check specifically, which the pre-existing T17 (zero-byte file) doesn't reach. Closes J4-07 (WEAK): every payload in this suite said "Edit", so a future Edit-only narrowing (or a weaker sentinel-emptiness check) would have failed open with no red. DOUBLY GATED per report §3.5 (edits config-protection's own test) + user's stated exception (STOP and show the exact draft before writing, even though the formal AUTHORIZATION line said AUTHORIZED) — drafted inline, user confirmed "proceed as drafted" before the sentinel/edit. Mutations (lean scratch copy — only hooks/config-protection.sh + this test file, not the whole repo/.git), one at a time, each reverted before the next: - T18/T19: gated the file_path extraction on `tool_name == "Edit"` (python3 tool_name check + if/else) → both red alone, everything else (incl. T1-T17) unaffected. - T20: swapped the whitespace-aware `grep -q '[^[:space:]]'` for `[ -n "$reason" ]` (byte-count only) → T20 reds alone; T17 (the zero-byte case) stays green either way, confirming T20 tests something T17 structurally cannot. GREEN: real repo unmutated, 24/24 passed, shellcheck clean. --- lib/tests/config-protection.test.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/tests/config-protection.test.sh b/lib/tests/config-protection.test.sh index 7f243bf..e56ce86 100755 --- a/lib/tests/config-protection.test.sh +++ b/lib/tests/config-protection.test.sh @@ -54,4 +54,21 @@ check T17-empty-refused "$?" 2 check T17-consumed "$([ -e "$tmp/.claude/.config-edit-ok" ] && echo present || echo gone)" gone rm -rf "$tmp" +# --- T18/T19: payload shapes beyond Edit (locks against future Edit-only narrowing) --- +c="$(mktemp -d)"; ( cd "$c" && printf \ + '{"tool_name":"Write","tool_input":{"file_path":"/x/doctor.sh","content":"x"}}' | bash "$H" ) \ + >/dev/null 2>&1; check T18-write-payload "$?" 2; rm -rf "$c" + +c="$(mktemp -d)"; ( cd "$c" && printf \ + '{"tool_name":"MultiEdit","tool_input":{"file_path":"/x/doctor.sh","edits":[{"old_string":"a","new_string":"b"}]}}' | bash "$H" ) \ + >/dev/null 2>&1; check T19-multiedit-payload "$?" 2; rm -rf "$c" + +# --- T20: sentinel with ONLY whitespace bytes (not literally empty) -> refused + consumed --- +tmp="$(mktemp -d)"; mkdir -p "$tmp/.claude"; printf ' \n\t' > "$tmp/.claude/.config-edit-ok" +( cd "$tmp" && printf '{"tool_name":"Edit","tool_input":{"file_path":"/x/doctor.sh"}}' \ + | HOME="$tmp" bash "$H" ) >/dev/null 2>&1 +check T20-whitespace-only-refused "$?" 2 +check T20-whitespace-only-consumed "$([ -e "$tmp/.claude/.config-edit-ok" ] && echo present || echo gone)" gone +rm -rf "$tmp" + printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]