#!/usr/bin/env bash # Zero-token verification of pi-guard-base against a real pi process. # # Drives pi with `get_state` only, so no model call is billed. Checks the two # capability layers, the `read` override, skill reachability, path containment # (including symlink/traversal/absolute escapes) and truncation. # # Usage: shared/extensions/tests/run-guard-checks.sh set -uo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO="$(git -C "$HERE" rev-parse --show-toplevel)" PI_BIN="${PI_BIN:-/home/claw/.npm-global/bin/pi}" WORK="$(mktemp -d /tmp/pi-guard-checks.XXXXXX)" trap 'rm -rf "$WORK"' EXIT cp -r "$HERE/fixtures/." "$WORK/" mv "$WORK/secret-lookalike.txt" "$WORK/secret-lookalike.env" sed -e "s|__FIXTURES__|$WORK|g" \ -e "s|__GUARD_BASE__|$REPO/shared/extensions/pi-guard-base.ts|" \ "$HERE/guard-probe.ts.in" > "$WORK/guard-ext.ts" OUT="$(printf '{"id":"1","type":"get_state"}\n' | timeout 120 "$PI_BIN" \ --mode rpc --no-session --no-builtin-tools \ --no-extensions -e "$WORK/guard-ext.ts" \ --no-skills --skill "$WORK/.pi/skills/guard-skill" \ --no-prompt-templates --no-themes --approve \ --provider zenmux --model openai/gpt-5.6-luna 2>&1 >/dev/null | grep '^GUARD_')" echo "$OUT" echo fails=0 expect() { if grep -qxF "GUARD_$1" <<<"$OUT"; then echo " PASS $1" else echo " FAIL $1 (actual: $(grep "^GUARD_${1%%=*}=" <<<"$OUT" || echo ''))" fails=$((fails + 1)) fi } echo "== assertions ==" expect 'ACTIVE=["probe_noop","read"]' expect 'READ_SOURCE=["cli"]' expect 'SP_HAS_SKILLS=true' expect 'SP_SKILLNAMES=["guard-skill"]' expect 'INSIDE_same=true' expect 'INSIDE_child=true' expect 'INSIDE_escape=false' expect 'INSIDE_prefix_trap=false' expect 'RESOLVE_skill=ALLOW' expect 'RESOLVE_outside=DENY' expect 'RESOLVE_traversal=DENY' expect 'RESOLVE_abs_home=DENY' expect 'TRUNC_applied=true len=true' expect 'REALPATH_missing_throws=yes' echo if [ "$fails" -eq 0 ]; then echo "RESULT: all checks passed"; else echo "RESULT: $fails check(s) failed"; fi exit $((fails > 0))