Build and Push Container / build-and-push (push) Successful in 23s
**Phase 03 final verification — all green** - Verified (all tasks already in `complete/`): snippet/Dockerfile/nginx.conf wiring, `scripts/check-headers.sh`, guardrail teeth, restored config byte-identical. - Tests (full suite): `./build.sh` → OK; `npm test` → **46 passed, 0 failed**; `bash .agents/validate.sh` → **exit 0** (46/46 + `HEADER CHECK PASSED` + hygiene gates); no leftover podman images. - Completion criteria: - `nginx-security-headers.inc` with 4 `add_header … always;` lines — ✅ - `nginx.conf`: 0 inline security `add_header` (cache headers only), 3/3 locations `include` snippet — ✅ - `server_tokens off;` in server block — ✅ (live proof: `Server: nginx`, no version) - `./scripts/check-headers.sh` → `HEADER CHECK PASSED`, OK on `/`, `/index.html`, hashed asset — ✅ (also confirmed all 4 headers + correct Cache-Control on 404 fallback) - `./build.sh && npm test` fully green — ✅ - No behavior change (terminal/vim/achievements/menu suites) — ✅ - Negative check re-run: dropping one `include` → `FAIL … on /style.<hash>.css`, exit 1 — ✅ - Docs: README.md & AGENTS.md reviewed in full against actual state (commands, ports 8123/18099, header mechanism, CI tags vs `.gitea/workflows/build-push.yml`, Node ≥20 engine floor, innerHTML/neofetch exception at `terminal.js:487`, simulated curl/wget guards) — **verified consistent, no changes needed**. - Notable: check-headers.sh aborts at the first failing path via `set -e` before printing `HEADER CHECK FAILED` — README's wording matches this exactly (verified empirically). PLAN.md's "44 tests" count is stale (now 46) but PLAN.md is locked — left for the user. - No live-infra changes (no HSTS in container; edge/Caddy untouched). - Next pending phase: `04_huntington_role`.
54 lines
1.8 KiB
Bash
Executable File
54 lines
1.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
# Verify every response class (/, /index.html, a hashed asset) carries the
|
|
# full security-header set, using the REAL nginx image on a scratch port.
|
|
# Requires a container runtime (podman or docker).
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
cd "$ROOT"
|
|
|
|
if command -v podman >/dev/null 2>&1; then RUNTIME=podman
|
|
elif command -v docker >/dev/null 2>&1; then RUNTIME=docker
|
|
else echo "ERROR: neither podman nor docker found" >&2; exit 2; fi
|
|
|
|
IMAGE="homepage-headercheck-$$"
|
|
PORT=18099
|
|
CID=""
|
|
trap 'if [ -n "$CID" ]; then $RUNTIME rm -f "$CID" >/dev/null 2>&1 || true; fi' EXIT
|
|
|
|
echo ">> building image ($RUNTIME)..."
|
|
$RUNTIME build -q -t "$IMAGE" .
|
|
|
|
echo ">> starting container on 127.0.0.1:$PORT..."
|
|
CID="$($RUNTIME run -d --rm -p 127.0.0.1:$PORT:8080 "$IMAGE")"
|
|
|
|
for i in $(seq 1 30); do
|
|
curl -sf -o /dev/null "http://127.0.0.1:$PORT/" && break
|
|
sleep 0.5
|
|
if [ "$i" -eq 30 ]; then echo "ERROR: container not ready" >&2; exit 1; fi
|
|
done
|
|
|
|
ASSET="$(curl -s "http://127.0.0.1:$PORT/" \
|
|
| grep -oE '(style|script|terminal)[a-z-]*\.[0-9a-f]{32}\.(css|js)' | head -n1 || true)"
|
|
if [ -z "$ASSET" ]; then echo "ERROR: no hashed asset found in index.html" >&2; exit 1; fi
|
|
|
|
fail=0
|
|
check_headers() {
|
|
local path="$1" hdrs h
|
|
hdrs="$(curl -sI "http://127.0.0.1:$PORT$path")"
|
|
for h in "X-Frame-Options" "X-Content-Type-Options" "Referrer-Policy" "Content-Security-Policy"; do
|
|
if ! printf '%s' "$hdrs" | grep -qi "^${h}:"; then
|
|
echo "FAIL: ${h} missing on ${path}" >&2; fail=1
|
|
fi
|
|
done
|
|
[ "$fail" -eq 0 ] && echo "OK: all security headers present on ${path}"
|
|
}
|
|
|
|
echo ">> checking headers..."
|
|
check_headers "/"
|
|
check_headers "/index.html"
|
|
check_headers "/${ASSET}"
|
|
|
|
if [ "$fail" -ne 0 ]; then echo "HEADER CHECK FAILED" >&2; exit 1; fi
|
|
echo "HEADER CHECK PASSED"
|