Test: Prove verified bootstrap boundaries
This commit is contained in:
@@ -64,8 +64,12 @@ case "\${1:-}" in
|
||||
[[ "\$#" -eq 5 && "\$5" == "$revision" ]] || exit 97
|
||||
cp "$work/fake-install" "$clone_dir/install"
|
||||
chmod +x "$clone_dir/install"
|
||||
printf '%s\n' '$revision' >"$clone_dir/.git/HEAD"
|
||||
elif [[ "\${4:-}" == -b ]]; then
|
||||
[[ "\$#" -eq 5 && "\$5" == main ]] || exit 97
|
||||
mkdir -p "$clone_dir/.git/refs/heads"
|
||||
printf '%s\n' '$revision' >"$clone_dir/.git/refs/heads/main"
|
||||
printf 'ref: refs/heads/main\n' >"$clone_dir/.git/HEAD"
|
||||
else
|
||||
exit 97
|
||||
fi
|
||||
@@ -196,6 +200,22 @@ grep -qF "install PANAMA_PATH=$clone_dir" "$calls" \
|
||||
grep -qE '^git (clone|.* pull)' "$calls" \
|
||||
&& note 'fresh bootstrap used mutable clone or pull behavior'
|
||||
|
||||
# A fresh fetch failure stops before any checked-out HEAD or executable
|
||||
# handoff exists. The empty initialized Git directory is not a usable checkout.
|
||||
configure_case fresh-fetch-failure
|
||||
run_boot "$revision" "$boot_sha"
|
||||
(( run_status != 0 )) || note 'fresh fetch failure returned success'
|
||||
grep -qF "git -C $clone_dir fetch --depth=1 origin $revision" "$calls" \
|
||||
|| note 'fresh fetch failure did not exercise the exact fetch'
|
||||
assert_no_install_or_rewrite 'fresh fetch failure'
|
||||
if grep -qE '^git .* (checkout|rev-parse)($| )' "$calls"; then
|
||||
note 'fresh fetch failure reported or materialized a checked-out HEAD'
|
||||
fi
|
||||
if [[ -x "$clone_dir/install" || -e "$clone_dir/.git/HEAD" \
|
||||
|| -e "$clone_dir/.git/refs/heads/main" ]]; then
|
||||
note 'fresh fetch failure left a usable checkout'
|
||||
fi
|
||||
|
||||
# A fetched checkout whose HEAD does not equal the requested commit never
|
||||
# creates the trusted branch or reaches install.
|
||||
configure_case head-mismatch "$mismatched_revision"
|
||||
|
||||
@@ -19,6 +19,8 @@ panama="$repo_dir/bin/panama"
|
||||
|
||||
findings=()
|
||||
note() { findings+=("$1"); }
|
||||
work="$(mktemp -d)"
|
||||
trap 'rm -rf -- "$work"' EXIT
|
||||
|
||||
# ── Verified bootstrap command ───────────────────────────────────────────────
|
||||
|
||||
@@ -88,6 +90,87 @@ if grep -qE 'curl[^|]*\|[[:space:]]*(bash|sh)|bash[[:space:]]+<\(curl' "$readme"
|
||||
note 'the README pipes a network response into a shell'
|
||||
fi
|
||||
|
||||
# Run the two exact documented blocks with a successful download and a failing
|
||||
# checksum. The Bash adapter records only the verified boot invocation; the
|
||||
# contract itself uses /usr/bin/bash so the adapter cannot hide this behavior.
|
||||
checksum_stub_dir="$work/checksum-bin"
|
||||
checksum_boot_calls="$work/checksum-boot-calls"
|
||||
mkdir -p "$checksum_stub_dir"
|
||||
|
||||
cat >"$checksum_stub_dir/curl" <<'STUB'
|
||||
#!/usr/bin/bash
|
||||
set -u
|
||||
destination=""
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--output)
|
||||
destination="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$destination" ]] || exit 97
|
||||
printf 'tampered boot bytes\n' >"$destination"
|
||||
STUB
|
||||
chmod +x "$checksum_stub_dir/curl"
|
||||
|
||||
cat >"$checksum_stub_dir/sha256sum" <<'STUB'
|
||||
#!/usr/bin/bash
|
||||
[[ "${1:-}" == -c ]] || exit 97
|
||||
exit 1
|
||||
STUB
|
||||
chmod +x "$checksum_stub_dir/sha256sum"
|
||||
|
||||
cat >"$checksum_stub_dir/bash" <<'STUB'
|
||||
#!/usr/bin/bash
|
||||
printf 'verified-boot %s\n' "$*" >>"$PANAMA_README_BOOT_CALLS"
|
||||
exit 0
|
||||
STUB
|
||||
chmod +x "$checksum_stub_dir/bash"
|
||||
|
||||
mapfile -d $'\036' -t bootstrap_snippets < <(
|
||||
awk '
|
||||
/^```sh$/ { in_block = 1; block = ""; next }
|
||||
/^```$/ && in_block {
|
||||
if (block ~ /bootstrap_commit=/) printf "%s%c", block, 30
|
||||
in_block = 0
|
||||
next
|
||||
}
|
||||
in_block { block = block $0 "\n" }
|
||||
' "$readme"
|
||||
)
|
||||
|
||||
checksum_failure_stops_boot() {
|
||||
local snippet="$1" status
|
||||
: >"$checksum_boot_calls"
|
||||
PATH="$checksum_stub_dir:/usr/bin:/bin" \
|
||||
PANAMA_README_BOOT_CALLS="$checksum_boot_calls" \
|
||||
/usr/bin/bash -c "$snippet" >/dev/null 2>&1
|
||||
status=$?
|
||||
(( status != 0 )) && [[ ! -s "$checksum_boot_calls" ]]
|
||||
}
|
||||
|
||||
if (( ${#bootstrap_snippets[@]} != 2 )); then
|
||||
note 'the README does not contain exactly two executable verified bootstrap blocks'
|
||||
else
|
||||
bootstrap_labels=(desktop server)
|
||||
for index in "${!bootstrap_snippets[@]}"; do
|
||||
snippet="${bootstrap_snippets[$index]}"
|
||||
label="${bootstrap_labels[$index]}"
|
||||
if ! checksum_failure_stops_boot "$snippet"; then
|
||||
note "the $label command invoked boot after checksum failure"
|
||||
fi
|
||||
|
||||
weakened_snippet="${snippet//$'set -euo pipefail\n'/}"
|
||||
if [[ "$weakened_snippet" == "$snippet" ]]; then
|
||||
note "the $label command has no fail-closed shell control to test"
|
||||
elif checksum_failure_stops_boot "$weakened_snippet"; then
|
||||
note "the $label checksum assertion accepts removal of fail-closed shell control"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ── The contract count ───────────────────────────────────────────────────────
|
||||
#
|
||||
# Counted the way `panama test` collects the suite, so the README agrees with
|
||||
|
||||
@@ -127,6 +127,7 @@ esac
|
||||
write_executable(stub_dir / "runuser", common + r'''
|
||||
log runuser "$@"
|
||||
[[ "${1:-}" == -u && "${2:-}" == gib && "${3:-}" == -- ]] || exit 97
|
||||
target_user="$2"
|
||||
shift 3
|
||||
if [[ "${1:-}" == install && "${2:-}" == -d && "${3:-}" == -m && "${4:-}" == 0700 && "${5:-}" == -- ]]; then
|
||||
/usr/bin/install "${@:2}"
|
||||
@@ -140,13 +141,17 @@ if [[ "${1:-}" == install && "${2:-}" == -m && "${3:-}" == 0600 && "${4:-}" == -
|
||||
>"$PANAMA_BOOT_FIXTURE_ROOT/state/target-key-meta"
|
||||
exit 0
|
||||
fi
|
||||
"$@"
|
||||
PANAMA_BOOT_TARGET_USER="$target_user" "$@"
|
||||
''')
|
||||
write_executable(stub_dir / "ssh-keygen", common + r'''
|
||||
log ssh-keygen "$@"
|
||||
exec /usr/bin/ssh-keygen "$@"
|
||||
''')
|
||||
write_executable(stub_dir / "git", common + r'''
|
||||
if [[ "${PANAMA_BOOT_TARGET_USER:-}" != gib ]]; then
|
||||
log git-rejected-direct "$@"
|
||||
exit 96
|
||||
fi
|
||||
log git "$@"
|
||||
case "${1:-}" in
|
||||
init)
|
||||
@@ -473,6 +478,7 @@ def configure_case(
|
||||
"signal-int-before-activation-cleanup-fails",
|
||||
"signal-int-during-candidate-preparation",
|
||||
"signal-term-during-backup-preparation",
|
||||
"direct-root-git-probe",
|
||||
):
|
||||
target_keys.write_text(TARGET_PUBLIC_KEY)
|
||||
elif name == "safe-root-key-copy":
|
||||
@@ -485,6 +491,29 @@ def configure_case(
|
||||
return fixture_root, stub_dir
|
||||
|
||||
|
||||
def assert_checkout_runs_as_target(name: str, calls: str, fixture_root: Path) -> None:
|
||||
call_lines = calls.splitlines()
|
||||
git_indices = [
|
||||
index for index, line in enumerate(call_lines) if line.startswith("git ")
|
||||
]
|
||||
if not git_indices:
|
||||
return
|
||||
|
||||
checkout = fixture_root / "home/gib/.local/share/Panama"
|
||||
expected_mkdir = f"runuser -u gib -- mkdir -p {checkout.parent} "
|
||||
if expected_mkdir not in call_lines:
|
||||
note(f"{name}: checkout parent directory was not created as the target user")
|
||||
|
||||
for index in git_indices:
|
||||
expected_runuser = f"runuser -u gib -- {call_lines[index]}"
|
||||
if index == 0 or call_lines[index - 1] != expected_runuser:
|
||||
note(f"{name}: checkout Git operation bypassed the selected target user")
|
||||
break
|
||||
|
||||
if any(line.startswith("git-rejected-direct ") for line in call_lines):
|
||||
note(f"{name}: checkout attempted a direct root Git operation")
|
||||
|
||||
|
||||
def run_case(
|
||||
name: str,
|
||||
*,
|
||||
@@ -614,6 +643,7 @@ fi
|
||||
note(f"{name}: bootstrap timed out, likely while reading an unsupported object")
|
||||
calls = (fixture_root / "calls").read_text()
|
||||
output = b"".join(chunks).decode(errors="replace")
|
||||
assert_checkout_runs_as_target(name, calls, fixture_root)
|
||||
return status, output, calls, fixture_root, process.pid
|
||||
|
||||
|
||||
@@ -652,6 +682,29 @@ else:
|
||||
note("actual-root-fixture-guard: boot mutated its rejected fixture root")
|
||||
|
||||
|
||||
probe_root, probe_stub_dir = configure_case("direct-root-git-probe")
|
||||
probe_checkout = probe_root / "home/gib/.local/share/Panama"
|
||||
probe_env = {
|
||||
**os.environ,
|
||||
"PATH": f"{probe_stub_dir}:/usr/bin:/bin",
|
||||
"PANAMA_BOOT_FIXTURE_ROOT": str(probe_root),
|
||||
"PANAMA_BOOT_REVISION": BOOT_REVISION,
|
||||
}
|
||||
direct_git = subprocess.run(
|
||||
[str(probe_stub_dir / "git"), "init", str(probe_checkout)],
|
||||
env=probe_env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
probe_calls = (probe_root / "calls").read_text()
|
||||
if direct_git.returncode != 96:
|
||||
note("direct-root-git-probe: Git adapter accepted a root-owned checkout call")
|
||||
if (probe_checkout / ".git").exists():
|
||||
note("direct-root-git-probe: rejected root-owned Git call mutated the checkout")
|
||||
if "git-rejected-direct init " not in probe_calls:
|
||||
note("direct-root-git-probe: fixture did not exercise the direct Git rejection")
|
||||
|
||||
|
||||
unsafe_cases = (
|
||||
"missing",
|
||||
"empty",
|
||||
|
||||
Reference in New Issue
Block a user