guard: close four fail-open holes found by independent review
ci/woodpecker/pr/ci Pipeline was canceled

An independent reviewer broke all three new controls before they shipped.
Every finding is reproduced as a fixture or a repro, because the class is
recurring rather than incidental: each hole was a case where the answer was
"allow" because something was ABSENT rather than because it was CHECKED.

1. wrapper-guard read only the spellings it knew. `curl -d@body` (no space),
   `--request=POST` (equals form), and a URL path assembled from shell
   variables each carried a real provider write straight through. Write
   detection now covers every body and method form curl accepts, and the
   endpoint match no longer anchors on a literal host path that a variable
   can dissolve.

2. wrapper-guard blocked only when the wrapper FILE existed. A host with a
   broken or partial install therefore permitted exactly the raw writes the
   guard exists to stop. Blocking is now on the endpoint; a missing wrapper
   changes the remedy text, not the verdict — a broken install is not
   permission to bypass gate 7.

3. mosaic-worktree read a worktree's safety from two questions, and a clean,
   fully-pushed tree holding a gitignored `local.secret` answered both with
   zero. `git worktree remove` then deleted the one copy in existence. A file
   is gitignored precisely so nothing else holds it, so ignored-but-not-
   disposable files are now a third evidence question. Build junk
   (node_modules, .venv, dist, caches, *.pyc) stays disposable, so the common
   case still reads SAFE.

4. check-tools-index counted a documented tool as discoverable at mode 0644.
   Every caller tests `[ -x ]`, so a non-executable tool is a missing tool;
   it now fails the gate with its own message.

Local gates green: sanitization, resident budget, test enumeration,
tools-index (4/4 self-test, 100% on the enforced git suite), and
wrapper-guard 20/20.
This commit is contained in:
Hermes Agent
2026-08-12 17:20:11 -05:00
parent 96609bdade
commit 8b7ac5b51e
4 changed files with 146 additions and 44 deletions
@@ -151,7 +151,7 @@ run_check() {
case "$suite" in _*) continue ;; esac
local total=0 found=0
local -a suite_missing=()
local -a suite_missing=() suite_noexec=()
for tool in "$dir"*.sh; do
[ -e "$tool" ] || continue
base="$(basename -- "$tool")"
@@ -159,6 +159,13 @@ run_check() {
total=$((total + 1))
if documented "$base"; then
found=$((found + 1))
# Documented AND present is not enough. The index presents these as
# commands to run, and every caller — the wrapper guard included —
# decides "is this tool here?" with `[ -x ]`. A 0644 wrapper is
# documented, present, and dead: it reads as absent to every check that
# matters while scoring 100% here. That is a false green, which is worse
# than a red, so it fails rather than warns.
[ -x "$tool" ] || suite_noexec+=("$base")
else
suite_missing+=("$base")
fi
@@ -166,11 +173,16 @@ run_check() {
[ "$total" -eq 0 ] && continue
local pct=$(( found * 100 / total ))
if [ "$enforced" -eq 1 ] && [ ${#suite_noexec[@]} -gt 0 ]; then
printf 'FAIL %-12s %3d%% (%d/%d) documented but not executable: %s\n' \
"$suite" "$pct" "$found" "$total" "${suite_noexec[*]}"
rc=1
fi
if [ "$enforced" -eq 1 ] && [ ${#suite_missing[@]} -gt 0 ]; then
printf 'FAIL %-12s %3d%% (%d/%d) undocumented: %s\n' \
"$suite" "$pct" "$found" "$total" "${suite_missing[*]}"
rc=1
elif [ "$enforced" -eq 1 ]; then
elif [ "$enforced" -eq 1 ] && [ ${#suite_noexec[@]} -eq 0 ]; then
printf 'ok %-12s %3d%% (%d/%d) [enforced]\n' "$suite" "$pct" "$found" "$total"
else
printf 'info %-12s %3d%% (%d/%d) not yet enforced\n' "$suite" "$pct" "$found" "$total"
@@ -222,6 +234,7 @@ self_test() {
mkdir -p "$tmp/tools/git"
printf '#!/bin/sh\n' > "$tmp/tools/git/documented-tool.sh"
printf '#!/bin/sh\n' > "$tmp/tools/git/test-ignored.sh"
chmod +x "$tmp/tools/git/documented-tool.sh" "$tmp/tools/git/test-ignored.sh"
# run_check reads the TOOLS_DIR / DOCS globals; an array cannot ride in a
# command-prefix assignment, so point the globals at the fixture directly.
@@ -231,18 +244,18 @@ self_test() {
# Case 1: fully documented -> pass.
printf 'see tools/git/documented-tool.sh for details\n' > "$tmp/doc.md"
if run_check >/dev/null; then
printf 'self-test 1/3 ok (complete index passes)\n'
printf 'self-test 1/4 ok (complete index passes)\n'
else
printf 'self-test 1/3 FAIL (complete index should pass)\n'; return 1
printf 'self-test 1/4 FAIL (complete index should pass)\n'; return 1
fi
# Case 2: an undocumented tool -> fail.
printf '#!/bin/sh\n' > "$tmp/tools/git/undocumented-tool.sh"
rc=0; run_check >/dev/null || rc=$?
if [ "$rc" -eq 1 ]; then
printf 'self-test 2/3 ok (undocumented tool fails the gate)\n'
printf 'self-test 2/4 ok (undocumented tool fails the gate)\n'
else
printf 'self-test 2/3 FAIL (undocumented tool should fail, got rc=%s)\n' "$rc"; return 1
printf 'self-test 2/4 FAIL (undocumented tool should fail, got rc=%s)\n' "$rc"; return 1
fi
# Case 3: a stale index reference -> fail.
@@ -250,12 +263,26 @@ self_test() {
printf 'also tools/git/deleted-tool.sh\n' >> "$tmp/doc.md"
rc=0; run_check >/dev/null || rc=$?
if [ "$rc" -eq 1 ]; then
printf 'self-test 3/3 ok (stale index reference fails the gate)\n'
printf 'self-test 3/4 ok (stale index reference fails the gate)\n'
else
printf 'self-test 3/3 FAIL (stale reference should fail, got rc=%s)\n' "$rc"; return 1
printf 'self-test 3/4 FAIL (stale reference should fail, got rc=%s)\n' "$rc"; return 1
fi
printf '\nself-test passed: the gate demonstrably reds on both drift directions.\n'
# Case 4: documented, present, and NOT executable -> fail. Found by an
# independent reviewer: a 0644 wrapper scored 100% here while reading as
# absent to every `[ -x ]` in the fleet, including the wrapper guard's.
sed -i '/deleted-tool/d' "$tmp/doc.md"
printf '#!/bin/sh\n' > "$tmp/tools/git/noexec-tool.sh"
chmod 0644 "$tmp/tools/git/noexec-tool.sh"
printf 'and tools/git/noexec-tool.sh\n' >> "$tmp/doc.md"
rc=0; run_check >/dev/null || rc=$?
if [ "$rc" -eq 1 ]; then
printf 'self-test 4/4 ok (documented but non-executable tool fails the gate)\n'
else
printf 'self-test 4/4 FAIL (non-executable tool should fail, got rc=%s)\n' "$rc"; return 1
fi
printf '\nself-test passed: the gate demonstrably reds on every drift direction.\n'
}
if [ "$SELF_TEST" -eq 1 ]; then