Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
6 changes: 6 additions & 0 deletions .jules/warden.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
39 changes: 20 additions & 19 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() !== '';
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down
Loading