T09 · Insecure Skill Coding Practices
- Location
api-server/server.js:36- Finding
Unbounded HTTP Request Body Enables Remote Memory Exhaustion
- Content
View full analysis
Vulnerability Details
File Location:
api-server/server.js:36-48
Vulnerability Type: Unbounded request-body buffering and denial of service
Risk Level: MediumVulnerable Code
js async function parseBody(req) { return new Promise((resolve, reject) => { let body = ''; req.on('data', chunk => body += chunk); req.on('end', () => { try { resolve(body ? JSON.parse(body) : {}); } catch (e) { reject(new Error('Invalid JSON')); } }); req.on('error', reject); }); }Technical Analysis
The HTTP server appends every received chunk to an in-memory string without enforcing a maximum body size, validating
Content-Length, imposing a request deadline, or stopping slow and incomplete uploads.Each POST request therefore permits attacker-controlled memory consumption. Once the body is received,
JSON.parsecreates an additional in-memory representation, and the supplied text is subsequently processed by multiple regular expressions and statistical-analysis routines. These operations can amplify both memory and CPU consumption.The server can be deployed for external integrations and does not implement authentication or application-level rate limiting. Consequently, any network client able to reach the service can exercise the vulnerable parser.
Attack Path
- An operator deploys the HTTP API on a network-accessible interface or behind a public proxy.
- An attacker connects to
/api/score,/api/analyze,/api/humanize, or/api/stats. - The attacker sends a very large JSON request body or keeps streaming chunks without completing the request.
- The server continually concatenates the chunks into the
bodystring. - One large request, or several concurrent requests, exhausts available heap memory and consumes event-loop resources.
- The Node.js process becomes unresponsive or terminates with an out-of-memory e ...[truncated 403 chars]
- Remediation
View remediation
Remediation Suggestions
- Enforce a conservative byte limit while streaming the request, such as 256 KB or another limit appropriate for expected documents.
- Track bytes using
Buffer.byteLengthrather than JavaScript string length. - Reject requests whose declared
Content-Lengthexceeds the configured limit. - Stop processing and destroy the request as soon as the streaming limit is exceeded.
- Return HTTP
413 Payload Too Largerather than a generic server error. - Configure header and request timeouts to prevent indefinitely slow uploads.
- Apply reverse-proxy limits, concurrency controls, and per-client rate limiting.
- Consider processing exceptionally large documents through an authenticated asynchronous job interface.
Example hardening pattern:
js const MAX_BODY_BYTES = 256 * 1024; async function parseBody(req) { return new Promise((resolve, reject) => { const declaredLength = Number(req.headers['content-length'] || 0); if (declaredLength > MAX_BODY_BYTES) { const error = new Error('Request body too large'); error.status = 413; reject(error); req.destroy(); return; } let body = ''; let received = 0; req.on('data', chunk => { received += chunk.length; if (received > MAX_BODY_BYTES) { const error = new Error('Request body too large'); error.status = 413; reject(error); req.destroy(); return; } body += chunk.toString('utf8'); }); req.on('end', () => { try { resolve(body ? JSON.parse(body) : {}); } catch { const error = new Error('Invalid JSON'); error.status = 400; reject(error); } }); req.on('error', reject); }); }
