T09 · Insecure Skill Coding Practices
- Location
scripts/get-daily.js:29- Finding
Unbounded Buffering of Remote API Responses
- Content
View full analysis
{ let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { ``` `scripts/get-article.js:35-41`: ```js https.get(url, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { ``` ### Technical Analysis Both scripts accumulate the complete response from `api.cjiot.cc` in a JavaScript string before parsing it. They do not impose a maximum response size, configure a request timeout, inspect `Content-Length`, or abort slow and oversized responses. The use of HTTPS protects the connection against ordinary network modification when certificate validation succeeds, but it does not protect the client from a compromised, malicious, or malfunctioning API server. A server capable of returning an arbitrarily large response can cause the Node.js process to continue allocating memory until the response ends or the process reaches its memory limit. The scripts also do not reject unexpected HTTP status codes or content types before buffering the body. Consequently, large error pages and other non-JSON responses are subject to the same unbounded buffering behavior. ### Attack Path 1. An attacker compromises the configured API service, its hosting environment, or another trusted component capable of controlling its HTTPS responses. 2. A user invokes `get-daily.js` or `get-article.js`. 3. The controlled endpoint returns a very large response, or sends data continuously without completing the response. 4. Each incoming chunk is appended to the `data` string. 5. Memory and connection resources continue to be consumed until the process is terminated, the host ...[truncated 598 chars]- Remediation
View remediation
{ if (res.statusCode !== 200) { res.resume(); console.error(`Unexpected HTTP status: ${res.statusCode}`); process.exitCode = 1; return; } const contentType = res.headers['content-type'] || ''; if (!contentType.includes('application/json')) { res.resume(); console.error('Unexpected response content type'); process.exitCode = 1; return; } let received = 0; let data = ''; res.on('data', (chunk) => { received += chunk.length; if (received > MAX_RESPONSE_BYTES) { res.destroy(new Error('API response exceeds the size limit')); return; } data += chunk; }); res.on('error', (error) => { console.error(`Response failed: ${error.message}`); }); res.on('end', () => { // Parse the bounded response. }); }); request.setTimeout(10_000, () => { request.destroy(new Error('API request timed out')); }); ``` ]]>
