diff --git a/.jules/bolt.md b/.jules/bolt.md index 00473e0..f82e4d0 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -172,3 +172,6 @@ In highly trafficked functions such as `isValidModel`, `isValidMessagesArray`, a Action: Optimized validation helper logic in `src/index.js` to strictly rely on explicit type checks and avoid double negations (`!!`). Simplified truthiness evaluations into direct equality checks (`trim() !== ''` instead of `!!trim()`). +2026-04-25 — DoS Mitigation via Route-Specific Parsing +Learning: Global `express.json()` middleware forces the application to buffer and parse request bodies even for unknown or non-existent routes, exposing a Denial of Service (DoS) vulnerability via large payloads to 404 endpoints. +Action: Apply `express.json()` strictly as route-specific middleware to the exact endpoints that require body parsing, and ensure dependent JSON error handlers are positioned correctly after those specific route definitions in the middleware chain. diff --git a/.jules/warden.md b/.jules/warden.md index 5e62cd9..606cf29 100644 --- a/.jules/warden.md +++ b/.jules/warden.md @@ -126,3 +126,9 @@ Observation / Pruned: Assessed repository state following previous optimizations. Since no new functional or architectural changes were introduced by the prior agent run, no new release cut or version bump is warranted. Maintained semantic integrity by preserving the existing v1.1.23 state. Alignment / Deferred: Release deferred. Repository state verified and stable. + +2026-04-25 — Assessment & Lifecycle +Observation / Pruned: +Assessed JULES/BOLT's optimization changing global `express.json` middleware into a route-specific middleware. This prevents unhandled routes (e.g. 404s) from attempting to buffer and parse large JSON payloads, saving CPU cycles and mitigating DoS vectors. The JSON error handler was effectively moved correctly to preserve functionality. Ran full tests and robustness scripts to verify correct validation edge cases pass. Zero unused files or exports were identified for pruning. +Alignment / Deferred: +Appended release notes for performance and security patch. Version bumped to 1.1.24. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3021c17..4d0bd5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ -# Changelog +## v1.1.24 - 2026-04-25 +### Changed +- **Security/Performance:** Modified the `express.json()` middleware to act as a route-specific middleware on `/v1/chat/completions` rather than globally. This prevents unnecessary JSON parsing for non-existent endpoints (like 404 routes), mitigating potential CPU exhaustion DoS vectors from large arbitrary payloads. -All notable changes to this project will be documented in this file. ## [1.1.23] - 2026-04-24 ### Performance diff --git a/package-lock.json b/package-lock.json index 43d5639..76c4c8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "one-api", - "version": "1.1.23", + "version": "1.1.24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "one-api", - "version": "1.1.23", + "version": "1.1.24", "license": "MIT", "dependencies": { "compression": "^1.8.1", diff --git a/package.json b/package.json index 8c21516..aac6001 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "one-api", - "version": "1.1.23", + "version": "1.1.24", "description": "One API to rule them all. Unified gateway for 20+ LLM providers. OpenAI-compatible, single binary, zero config.", "main": "src/index.js", "scripts": { diff --git a/src/index.js b/src/index.js index 64ce407..34747e0 100644 --- a/src/index.js +++ b/src/index.js @@ -48,28 +48,10 @@ app.get('/health', (req, res) => { // Compress all responses to reduce bandwidth and latency app.use(compression()); -// Set a larger JSON limit since LLM contexts can be quite large -app.use(express.json({ limit: '10mb' })); const ERROR_INVALID_JSON = Buffer.from(JSON.stringify({ error: 'Invalid JSON payload' })); const ERROR_PAYLOAD_TOO_LARGE = Buffer.from(JSON.stringify({ error: 'Payload too large' })); -// Handle invalid JSON gracefully -app.use((err, req, res, next) => { - if (res.headersSent) { - return next(err); - } - if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { - res.setHeader('Content-Type', 'application/json; charset=utf-8'); - return res.status(400).send(ERROR_INVALID_JSON); - } - if (err.type === 'entity.too.large') { - res.setHeader('Content-Type', 'application/json; charset=utf-8'); - return res.status(413).send(ERROR_PAYLOAD_TOO_LARGE); - } - next(err); -}); - // API endpoints function isValidModel(model) { return typeof model === 'string' && model.trim() !== ''; @@ -108,7 +90,10 @@ const ERROR_MISSING_MESSAGES = Buffer.from(JSON.stringify({ error: 'Missing or i const ERROR_TOO_MANY_MESSAGES = Buffer.from(JSON.stringify({ error: 'Too many messages' })); const ERROR_MALFORMED_MESSAGE = Buffer.from(JSON.stringify({ error: 'Malformed message object' })); -app.post('/v1/chat/completions', (req, res) => { +// Set a larger JSON limit since LLM contexts can be quite large +const jsonParser = express.json({ limit: '10mb' }); + +app.post('/v1/chat/completions', jsonParser, (req, res) => { const { model, messages } = req.body || {}; if (!isValidModel(model)) { return res.status(400).send(ERROR_MISSING_MODEL); @@ -131,6 +116,22 @@ app.post('/v1/chat/completions', (req, res) => { res.status(200).send(payload); }); +// Handle invalid JSON gracefully +app.use((err, req, res, next) => { + if (res.headersSent) { + return next(err); + } + if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + return res.status(400).send(ERROR_INVALID_JSON); + } + if (err.type === 'entity.too.large') { + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + return res.status(413).send(ERROR_PAYLOAD_TOO_LARGE); + } + next(err); +}); + const ERROR_NOT_FOUND = Buffer.from(JSON.stringify({ error: 'Not found' })); // 404 handler — return JSON for unknown routes