shoc-pr-review-runner/tests/test-gate-integrity.sh
Adam Moussa c3cd8f7765
feat: SHOC PR review runner, phase 1
Manually-dispatched GitHub Actions workflow that reviews SHOC pull requests in
a clean environment: exact-head checkout of shoc-frontend-new and shoc-backend,
clean build/test gates, a truthful evidence report, a single-shot Fireworks
review, deterministic output validation, and published artifacts. The runner
never writes to the product repositories or their pull requests.

The review checklists move here from the reviewers' local Cursor commands so
the instructions live outside both product repos.

Phase 1 does not provision a database, start either application, or run live
browser flows; the evidence report records those as NOT_RUN so a review cannot
claim them.

Security architecture: building a PR executes its author's code, so the
workflow is split. The gates job runs that code holding no Fireworks key and
revokes its App token first; the review job holds the key, executes no product
code, and re-checks out this repo fresh. Product checkouts live outside the
workspace, the App token is downscoped at mint time, gate results fail closed
on any duplicate key, changed files are read from git objects rather than the
filesystem, and the validator re-checks every claim against the gate table.
2026-07-29 12:05:38 -04:00

84 lines
3.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# Regression test for the gate-forgery attack confirmed by the pre-push
# security review.
#
# Threat: the build gates execute code authored in the PR under review. That
# code runs as the same user and can write the gate table, either overriding a
# genuine FAIL or pre-seeding a PASS for a gate that has not run yet. Either
# way it forges the exact signal the runner exists to produce.
#
# Defense under test: the runner records each key exactly once, so any duplicate
# key means a second writer touched the table, and every decision path calls
# assert_gate_table_intact first and fails closed.
set -euo pipefail
TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(cd "$TESTS_DIR/.." && pwd)"
pass=0
fail=0
check() { # check <name> <expected: ok|err> <actual-rc>
local name="$1" expect="$2" rc="$3" got="ok"
[ "$rc" -eq 0 ] || got="err"
if [ "$got" = "$expect" ]; then
pass=$((pass + 1))
else
echo "FAIL: $name (expected $expect, got $got, rc=$rc)"
fail=$((fail + 1))
fi
}
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
mkdir -p "$tmp/artifacts" "$tmp/state"
export WORKSPACE_DIR="$tmp"
export ARTIFACTS_DIR="$tmp/artifacts"
export STATE_DIR="$tmp/state"
export GATE_STATUS_FILE="$tmp/state/gate-status.tsv"
# shellcheck source=../scripts/lib.sh
source "$REPO_DIR/scripts/lib.sh"
# 1. An untampered table passes.
record_gate frontend.install PASS "log"
record_gate frontend.lint FAIL "exit 1"
rc=0; ( assert_gate_table_intact ) >/dev/null 2>&1 || rc=$?
check "clean table passes integrity check" ok "$rc"
# 2. Pre-seeding: PR code writes a forged PASS for a gate that has not run yet,
# then the runner records the genuine FAIL. First-wins would return the
# forged PASS, so the duplicate must be detected.
printf 'frontend.build\tPASS\tforged by PR code\n' >>"$GATE_STATUS_FILE"
record_gate frontend.build FAIL "exit 2"
rc=0; ( assert_gate_table_intact ) >/dev/null 2>&1 || rc=$?
check "pre-seeded forged PASS is detected" err "$rc"
# 3. The same tampered table must block a review decision.
cp "$TESTS_DIR/fixtures/artifacts/frontend-pr.json" "$ARTIFACTS_DIR/"
rc=0
env -i PATH="$PATH" HOME="$HOME" \
WORKSPACE_DIR="$tmp" ARTIFACTS_DIR="$tmp/artifacts" STATE_DIR="$tmp/state" \
REVIEW_TYPE=frontend \
"$REPO_DIR/scripts/validate-review-output.sh" \
"$TESTS_DIR/fixtures/reviews/valid-approve.md" >/dev/null 2>&1 || rc=$?
check "tampered table blocks review validation" err "$rc"
# 4. run_gate strips the Actions runner-command channels from the child
# environment, so PR code cannot poison later steps via $GITHUB_ENV.
probe="$tmp/probe.sh"
cat >"$probe" <<'PROBE'
#!/usr/bin/env bash
[ -z "${GITHUB_ENV:-}" ] || { echo "GITHUB_ENV leaked"; exit 1; }
[ -z "${GITHUB_PATH:-}" ] || { echo "GITHUB_PATH leaked"; exit 1; }
[ -z "${RUNNER_TEMP:-}" ] || { echo "RUNNER_TEMP leaked"; exit 1; }
[ -z "${GATE_STATUS_FILE:-}" ] || { echo "GATE_STATUS_FILE leaked"; exit 1; }
exit 0
PROBE
chmod +x "$probe"
rc=0
GITHUB_ENV="$tmp/ghenv" GITHUB_PATH="$tmp/ghpath" RUNNER_TEMP="$tmp" \
run_gate probe.env probe.log "$probe" >/dev/null 2>&1 || rc=$?
check "run_gate strips runner-command channels from PR code" ok "$rc"
echo "gate-integrity: $pass passed, $fail failed"
[ "$fail" -eq 0 ]