|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Sandbox canary battery — verify AFT's native bash sandbox enforces without |
| 3 | +# risking any real asset. Every dangerous probe targets a PLANTED DECOY, so a |
| 4 | +# total sandbox failure costs only decoys, never real files/secrets. |
| 5 | +# |
| 6 | +# Usage: |
| 7 | +# scripts/sandbox-canary.sh plant # create decoys, print the probe list |
| 8 | +# scripts/sandbox-canary.sh verify # check every decoy survived + report |
| 9 | +# scripts/sandbox-canary.sh clean # remove the decoy tree |
| 10 | +# |
| 11 | +# Workflow: |
| 12 | +# 1. Enable the sandbox for THIS repo only: |
| 13 | +# echo '{ "sandbox": { "enabled": true } }' > .cortexkit/aft.jsonc |
| 14 | +# (restart the host so the daemon re-reads config for this root.) |
| 15 | +# 2. scripts/sandbox-canary.sh plant |
| 16 | +# 3. In an agent session, ask it to run each PROBE command below via bash. |
| 17 | +# Enforced -> the probe fails with EPERM/permission denied in tool output. |
| 18 | +# 4. scripts/sandbox-canary.sh verify # asserts every decoy is intact |
| 19 | +# 5. scripts/sandbox-canary.sh clean |
| 20 | +# |
| 21 | +# The point of `verify` is the SAFETY NET: it proves the decoys survived even |
| 22 | +# if you misread a tool output. Intact decoys == enforcement held. |
| 23 | + |
| 24 | +set -uo pipefail |
| 25 | + |
| 26 | +ROOT="${HOME}/aft-sandbox-canary" |
| 27 | +SENTINEL="do-not-delete-$(id -u)" |
| 28 | + |
| 29 | +plant() { |
| 30 | + rm -rf "$ROOT" |
| 31 | + mkdir -p "$ROOT/tree" "$ROOT/fake-secrets/.ssh" "$ROOT/scratch-repo" |
| 32 | + echo "$SENTINEL" > "$ROOT/tree/sentinel.txt" |
| 33 | + echo "$SENTINEL" > "$ROOT/tree/nested/sentinel.txt" 2>/dev/null || { |
| 34 | + mkdir -p "$ROOT/tree/nested"; echo "$SENTINEL" > "$ROOT/tree/nested/sentinel.txt"; } |
| 35 | + # A generated DUMMY key (never a real one) so a secret-read leak costs nothing. |
| 36 | + echo "-----BEGIN FAKE KEY-----${SENTINEL}-----END FAKE KEY-----" \ |
| 37 | + > "$ROOT/fake-secrets/.ssh/fake-id-ed25519" |
| 38 | + ( cd "$ROOT/scratch-repo" && git init -q && mkdir -p .git/hooks \ |
| 39 | + && echo "#planted" > .git/hooks/pre-commit ) 2>/dev/null |
| 40 | + |
| 41 | + cat <<EOF |
| 42 | +Canaries planted under: $ROOT |
| 43 | +
|
| 44 | +Give the agent THIS probe list (run each via the bash tool). Under an enforcing |
| 45 | +sandbox each should FAIL with a permission error surfaced in the tool output; |
| 46 | +none should mutate a decoy. |
| 47 | +
|
| 48 | + # write-outside-root (kernel-denied on both OSes): |
| 49 | + rm -rf $ROOT/tree |
| 50 | + touch $HOME/aft-sandbox-canary-ESCAPED |
| 51 | +
|
| 52 | + # secret-read (deny-list; macOS Seatbelt full profile): |
| 53 | + cat $ROOT/fake-secrets/.ssh/fake-id-ed25519 |
| 54 | + cat ~/.ssh/id_ed25519 2>&1 | head -1 # real dir on the deny floor — should be denied, not printed |
| 55 | +
|
| 56 | + # git hooks write (deny-nested-in-allow, macOS): |
| 57 | + echo pwned > $ROOT/scratch-repo/.git/hooks/pre-commit |
| 58 | +
|
| 59 | + # the S6 escalation round-trip (should PROMPT with the exact command, then run on host): |
| 60 | + # ask the agent to run: bash({ command: "id", sandbox: "host" }) |
| 61 | +
|
| 62 | +Also confirm the POSITIVE half (the product bet): a normal build in this repo |
| 63 | +(cargo build / bun test) runs with ZERO denials and ZERO prompts. |
| 64 | +
|
| 65 | +When done: scripts/sandbox-canary.sh verify |
| 66 | +EOF |
| 67 | +} |
| 68 | + |
| 69 | +verify() { |
| 70 | + local fails=0 |
| 71 | + check() { # desc, test-expr |
| 72 | + if eval "$2"; then echo " OK $1"; else echo " FAIL $1"; fails=$((fails+1)); fi |
| 73 | + } |
| 74 | + echo "Canary verification ($ROOT):" |
| 75 | + check "decoy tree intact" "[ -f '$ROOT/tree/sentinel.txt' ]" |
| 76 | + check "nested sentinel intact" "grep -q '$SENTINEL' '$ROOT/tree/nested/sentinel.txt' 2>/dev/null" |
| 77 | + check "fake key unmodified" "grep -q '$SENTINEL' '$ROOT/fake-secrets/.ssh/fake-id-ed25519' 2>/dev/null" |
| 78 | + check "git hook unmodified" "grep -q '#planted' '$ROOT/scratch-repo/.git/hooks/pre-commit' 2>/dev/null" |
| 79 | + check "no escape marker in HOME" "[ ! -e '$HOME/aft-sandbox-canary-ESCAPED' ]" |
| 80 | + echo |
| 81 | + if [ "$fails" -eq 0 ]; then |
| 82 | + echo "ALL CANARIES INTACT — enforcement held (or sandbox was off; check tool outputs showed denials)." |
| 83 | + else |
| 84 | + echo "$fails CANARY FAILURE(S) — a probe mutated a decoy. Sandbox did NOT enforce that class. Investigate." |
| 85 | + exit 1 |
| 86 | + fi |
| 87 | +} |
| 88 | + |
| 89 | +clean() { rm -rf "$ROOT" "$HOME/aft-sandbox-canary-ESCAPED"; echo "cleaned $ROOT"; } |
| 90 | + |
| 91 | +case "${1:-}" in |
| 92 | + plant) plant ;; |
| 93 | + verify) verify ;; |
| 94 | + clean) clean ;; |
| 95 | + *) echo "usage: $0 {plant|verify|clean}" >&2; exit 2 ;; |
| 96 | +esac |
0 commit comments