Phase 1 security hardening: dead secrets files deleted, body limits, CSP, error redaction
Three commits closing the bulk of an upstream security audit. The most urgent finding - two dead frontend files that referenced god-mode credentials - is fixed first. The rest cover body size limits, zip/XML bomb guards, JWT email validation, a CSP policy, multer upgrade, and scrubbing raw Postgres errors from API responses.
C3 (critical) - dead secrets surface (204351ca, +9/-173 across 4 files). frontend/src/lib/supabase-server.ts and frontend/src/lib/storage.ts had zero imports but referenced SUPABASE_SECRET_KEY and R2_SECRET_ACCESS_KEY. Any future client-side import would have leaked both to every browser session. Both files are deleted. .env.local.example gains a header explicitly stating that service-role keys, database passwords, storage credentials, and AI provider keys belong in backend/.env only.
H2/H3/H4/H5/H7/M5/M6 (a46810a5, +365/-79 across 11 files). Several fixes in one:
- CORS now refuses to start in production without
FRONTEND_URLset. - Global JSON body limit drops from 50 MB to 1 MB; chat gets a 10 MB per-route override.
- LibreOffice DOCX conversion races against a 60-second timeout.
- New
backend/src/lib/safeZip.tsprovidesloadZipSafely(rejects archives with declared uncompressed size over 200 MB) andassertSafeXml(rejects payloads over 50 MB or containing a DOCTYPE or ENTITY declaration). All fourJSZip.loadAsynccall sites indocxTrackedChanges.tsmigrate to it. Standard XML entities in legitimate DOCX content continue to work. - JWT email is validated for shape before being interpolated into the PostgREST
csfilter onshared_with. - multer upgraded from 1.x to 2.x (1.x has documented DoS CVEs).
- Helmet's CSP goes from
contentSecurityPolicy: falsetodefault-src: 'none'; frame-ancestors: 'none'- correct for a JSON API surface.
M3/M4 (35712cbe, +87/-3571 across 10 files). Twenty-four routes across chat, projects, documents, tabular, workflows, user, and review-creation paths previously returned raw Postgres error messages. All 24 migrate to sendServerError, which logs the full error server-side and returns a generic message to the client. devLog and devWarn helpers in backend/src/lib/logger.ts no-op when NODE_ENV === "production" - all console.log calls in routes/documents.ts and lib/chatTools.ts are migrated. console.error is kept where appropriate so operational errors stay visible. backend/bun.lock and frontend/bun.lock are also deleted in this commit (PIP standardises on npm).
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?