import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk' /** * v1.1.0:9 — P2 hardening batch (2026-06-13, follows the :8 security batch). * * Input-validation, rate-limiting and container hardening from the full-eval * P2 queue (see EVALUATION.md / ROADMAP.md): * * - Malformed request bodies now return 400 instead of 500. New * lib/http.ts `readJsonBody` maps a bad JSON body to a ZodError across the * 11 body-parsing CRUD routes (which already map ZodError -> 400); * /api/me/import and /api/admin/signups guard it explicitly (safeParse * style). Invalid `date` and out-of-range/non-numeric pagination on * /api/workouts are likewise 400, not a Prisma 500. * - POST /api/auth (the raw login API) is now rate-limited with the same * per-IP 10/15min cap as the UI login server action, sharing the * `login:${ip}` bucket — previously an uncapped credential-stuffing * surface. Returns 429 + Retry-After. * - The rate limiter's client-IP detection now reads the rightmost * (trusted-proxy-appended) X-Forwarded-For entry instead of the spoofable * leftmost one, so a forged XFF can't rotate the limiter key. * - The container drops root: the entrypoint still prepares /data as root, * then chowns it to `nextjs` and `exec su-exec`s the Node server as the * unprivileged uid 1001 — shrinking the blast radius of any app RCE. * * App-code + packaging only — no schema, no API contract change for existing * data, no data migration. Existing /data survives untouched. */ export const v_1_1_0_9 = VersionInfo.of({ version: '1.1.0:9', releaseNotes: { en_US: 'Hardening. Bad or malformed requests now return clean validation errors instead of server errors. The login API is rate-limited (matching the web form) and the limiter can no longer be fooled by spoofed forwarding headers. The app container now runs as an unprivileged user instead of root, reducing the impact of any future vulnerability. No schema or data changes — your existing data is untouched.', }, migrations: { up: async () => {}, down: IMPOSSIBLE, }, })