v1.1.0:9 — P2 hardening: input-validation 400s, auth rate-limit, XFF anti-spoof, non-root container
CI / proof-of-work (Next.js app) (push) Has been cancelled
CI / start9/0.4 (StartOS package code) (push) Has been cancelled

P2 batch from the 2026-06-13 full-eval (EVALUATION.md / ROADMAP.md), reviewed by the reviewer agent. App-code + packaging only; no schema or data change, existing /data untouched.

Input validation: malformed JSON bodies, invalid date, and out-of-range or non-numeric pagination on /api/workouts now return 400 instead of 500. New lib/http.ts readJsonBody maps a bad body to a ZodError across the 11 CRUD routes whose catch maps ZodError to 400; me/import and admin/signups guard request.json() in an explicit try/catch.

Rate limiting: POST /api/auth now shares the UI login server action's per-IP 10-per-15min cap and returns 429 + Retry-After. clientIpFromHeaders reads the rightmost (trusted-proxy-appended) X-Forwarded-For entry instead of the spoofable leftmost.

Container: drops root. The entrypoint prepares /data as root, chowns it to nextjs, then exec su-exec nextjs:nodejs node server.js (su-exec added to the runner image). The container drop needs live sideload verification.
This commit is contained in:
Keysat
2026-06-13 00:03:47 -05:00
parent 988a3cca9a
commit 3f22ef7600
23 changed files with 365 additions and 41 deletions
+14 -3
View File
@@ -330,12 +330,23 @@ if [ -f "$TEMPLATES_JSON_PATH" ] && [ -f "$TEMPLATES_SCRIPT" ] && [ -f "$DB_PATH
fi
# -----------------------------------------------------------------------------
# Step 4 — launch the app.
# Step 4 — launch the app as the unprivileged `nextjs` user.
#
# Everything above runs as root because the entrypoint has to prepare /data
# — a StartOS-mounted volume whose runtime ownership we don't control at
# build time — by creating the DB, running the ALTERs and reconciling the
# library. Now that the data layer is ready we hand /data to `nextjs` and
# drop privileges via su-exec, so the long-lived, remote-facing Node server
# never runs as root (shrinks the blast radius of any RCE in the app).
# -----------------------------------------------------------------------------
export DATABASE_URL="file:$DB_PATH"
export NODE_ENV="${NODE_ENV:-production}"
export HOSTNAME="${HOSTNAME:-0.0.0.0}"
export PORT="${PORT:-3000}"
log "launching Next.js on :${PORT} with DATABASE_URL=file:${DB_PATH}"
exec node /app/server.js
# Make every file the root-run setup just created in /data writable by the
# app user. Guarded so a chown hiccup logs rather than aborts boot.
chown -R nextjs:nodejs "$DATA_DIR" 2>/dev/null || log "WARN: could not chown $DATA_DIR; continuing"
log "launching Next.js on :${PORT} as nextjs with DATABASE_URL=file:${DB_PATH}"
exec su-exec nextjs:nodejs node /app/server.js