Mercurial
diff mrjunejune/test/config_validation_test.sh @ 264:04fee26ecce0
add authenticated JRPG conversation platform
Add reusable auth/session storage, owned conversation recovery, guest quotas, admin workflows, URL-routed conversation UI, mobile frame support, and parallel browser acceptance.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 07 Aug 2026 07:34:12 -0700 |
| parents | |
| children | 056790c4fb0d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/config_validation_test.sh Fri Aug 07 07:34:12 2026 -0700 @@ -0,0 +1,247 @@ +#!/usr/bin/env bash +# config_validation_test.sh — startup config validation tests. +# +# Verifies that the server binary exits 1 (fail-closed) on bad config, +# starts cleanly on env-only config (no .config file), and enforces the +# loopback requirement for AUTH_DEV_INSECURE_COOKIE. +# +# Bazel passes the server binary path as $1. +set -euo pipefail + +server="$(realpath "$1")" +tmpdir="$(mktemp -d)" +trap 'rm -rf "$tmpdir"' EXIT + +pass() { printf " %-62s PASS\n" "$1"; } +fail() { echo "FAIL: $1" >&2; exit 1; } + +# A valid 64-hex-char secret (32 random bytes) for success cases. +GOOD_SECRET="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + +# Helper: run $@ as a command, expect it to exit non-zero ≤ 3 seconds. +run_expect_fail() { + local label="$1"; shift + local out + set +e + out=$(timeout 3 "$@" 2>&1) + local code=$? + set -e + if [[ $code -eq 0 || $code -eq 124 ]]; then + echo "FAIL: '$label' — expected exit 1, got code $code" >&2 + echo "$out" >&2 + exit 1 + fi + pass "$label" +} + +# Helper: run $@ as a command; expect it to emit an "Initialised" or +# listen/startup log line within 5 seconds, then kill it. +run_expect_start() { + local label="$1"; shift + local logfile="$tmpdir/start_${RANDOM}.log" + set +e + timeout 5 "$@" >"$logfile" 2>&1 & + local pid=$! + local started=false + for _ in $(seq 1 50); do + sleep 0.1 + if grep -q -E 'Initialised|Listening|WTF is going on' "$logfile" 2>/dev/null; then + started=true + break + fi + if ! kill -0 "$pid" 2>/dev/null; then + break + fi + done + kill "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true + set -e + if [[ "$started" != "true" ]]; then + echo "FAIL: '$label' — server did not start (log below)" >&2 + cat "$logfile" >&2 + exit 1 + fi + pass "$label" +} + +echo "" +echo "[config validation]" + +# ── Fail cases ───────────────────────────────────────────────────────────────── + +# Missing secret → exit 1 +run_expect_fail "missing AUTH_COOKIE_SECRET → exit 1" \ + env -i "TEST_TMPDIR=$tmpdir" "$server" + +# Secret too short (62 hex chars = 31 bytes) +run_expect_fail "short AUTH_COOKIE_SECRET (62 hex) → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Secret with non-hex characters +run_expect_fail "non-hex AUTH_COOKIE_SECRET → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# AUTH_DEV_INSECURE_COOKIE=true with non-loopback SERVER_HOST → exit 1 +run_expect_fail "insecure cookie without loopback SERVER_HOST → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=0.0.0.0" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# AUTH_DEV_INSECURE_COOKIE=true with no SERVER_HOST → exit 1 +run_expect_fail "insecure cookie without any SERVER_HOST → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Bootstrap username set without hash → exit 1 +run_expect_fail "bootstrap username without hash → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_BOOTSTRAP_USERNAME=admin" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Bootstrap hash with wrong format → exit 1 +run_expect_fail "bootstrap hash with wrong format → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_BOOTSTRAP_USERNAME=admin" \ + "AUTH_BOOTSTRAP_PASSWORD_HASH=not-a-real-hash" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Bootstrap hash with partial/truncated format → exit 1 +run_expect_fail "bootstrap hash truncated (only prefix) → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_BOOTSTRAP_USERNAME=admin" \ + "AUTH_BOOTSTRAP_PASSWORD_HASH=zenbu-scrypt\$v=1\$N=32768\$r=8\$p=1\$" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# idle TTL > abs TTL → exit 1 +run_expect_fail "idle TTL exceeds abs TTL → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_SESSION_IDLE_TTL=9999999" \ + "AUTH_SESSION_ABS_TTL=100" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Malformed AUTH_SESSION_IDLE_TTL (non-integer) → exit 1 +run_expect_fail "malformed AUTH_SESSION_IDLE_TTL → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_SESSION_IDLE_TTL=3600abc" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Malformed AUTH_SESSION_ABS_TTL (non-integer) → exit 1 +run_expect_fail "malformed AUTH_SESSION_ABS_TTL → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_SESSION_ABS_TTL=not_a_number" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Malformed S3_URL_EXPIRES (non-integer) → exit 1 +run_expect_fail "malformed S3_URL_EXPIRES → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "S3_URL_EXPIRES=3600x" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# S3_URL_EXPIRES = 0 (not positive) → exit 1 +run_expect_fail "S3_URL_EXPIRES=0 (non-positive) → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "S3_URL_EXPIRES=0" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Trusted proxy with invalid chars → exit 1 +run_expect_fail "invalid trusted proxy (bad chars) → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_TRUSTED_PROXY=not an ip!" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Trusted proxy with out-of-range IPv4 octet (999.999.999.999) → exit 1 +run_expect_fail "trusted proxy 999.999.999.999 → exit 1" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_TRUSTED_PROXY=999.999.999.999" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# ── Success cases ───────────────────────────────────────────────────────────── +# env-only startup (no .config file) with loopback host +run_expect_start "env-only startup (no .config) with loopback host" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "MRJUNEJUNE_PORT=16969" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Valid IPv4 trusted proxy is accepted +run_expect_start "valid IPv4 trusted proxy accepted" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_TRUSTED_PROXY=10.0.0.1" \ + "MRJUNEJUNE_PORT=16970" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +# Valid IPv6 trusted proxy (canonical ::1) is accepted +run_expect_start "valid IPv6 trusted proxy (::1) accepted" \ + env -i \ + "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ + "SERVER_HOST=127.0.0.1" \ + "AUTH_DEV_INSECURE_COOKIE=true" \ + "AUTH_TRUSTED_PROXY=::1" \ + "MRJUNEJUNE_PORT=16971" \ + "TEST_TMPDIR=$tmpdir" \ + "$server" + +echo ""