diff --git a/server.js b/server.js index 27659a5..1071083 100644 --- a/server.js +++ b/server.js @@ -102,7 +102,19 @@ app.use(cors({ optionsSuccessStatus: 200, })); -app.use(express.json()); +// Body size limit. The default in express.json() is 100kb; we make +// it explicit + env-tunable. Capping the body size is a basic +// defense against memory-exhaustion DoS — even an unauthenticated +// caller can otherwise force the server to buffer arbitrarily +// large JSON strings before the parser can reject them. +// +// 100kb is comfortably above any expected payload here (the largest +// real body is a TimeEntry create with a teDescription, capped at +// 10000 chars in the zod schema). Operators with unusual needs can +// override via JSON_BODY_LIMIT=512kb (etc.). +app.use(express.json({ + limit: process.env.JSON_BODY_LIMIT || '100kb', +})); // Rate limit the v1 surface to defend against authKey brute-force. // Defaults: 100 requests / 15-minute window per IP. Operators can diff --git a/tests/api/body-size-limit.test.js b/tests/api/body-size-limit.test.js index 1e82de4..52df134 100644 --- a/tests/api/body-size-limit.test.js +++ b/tests/api/body-size-limit.test.js @@ -22,6 +22,11 @@ beforeAll(async () => { const router = (await import('../../app/routers/router.js')).default || require('../../app/routers/router.js'); // Use a tiny 1kb limit so we can trip it without sending megabytes. + // This is the same shape server.js uses — `express.json({limit})` + // — just with a smaller cap for test ergonomics. Doesn't exercise + // server.js's JSON_BODY_LIMIT env hook directly (test-time env + // mutation is fragile under vitest's ESM module cache), but does + // cover the underlying parser behavior that server.js relies on. app = express(); app.use(express.json({ limit: '1kb' })); app.use('/', router);