mirror of
https://github.com/Sea-Haven-Industries/shoc-pr-review-runner.git
synced 2026-09-30 04:43:12 +00:00
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.
84 lines
3.2 KiB
Bash
Executable file
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 ]
|