T09 · Insecure Skill Coding Practices
Warning
- Location
- index.js:48
- Finding
- Unbounded Response Buffering and Unsafe Terminal Rendering of Remote Content## Vulnerability Details **File Location**: `index.js:48-65, 74-87, 109-116` **Vulnerability Type**: Unbounded resource consumption and terminal control-sequence injection **Risk Level**: Medium ### Vulnerable Code ```js const fetchTrends = (targetUrl) => { return new Promise((resolve, reject) => { const req = https.get(targetUrl, { headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } }, (res) => { if (res.statusCode === 404) { reject(new Error(`Country '${countrySlug}' not found.`)); return; } if (res.statusCode !== 200) { reject(new Error(`Failed to fetch trends. Status Code: ${res.statusCode}`)); return; } let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', () => resolve(data)); }); req.on('error', (e) => reject(e)); }); }; ``` ```js const html = await fetchTrends(url); const $ = cheerio.load(html); const trends = []; $('table.trends tbody tr').each((i, el) => { const name = $(el).find('.main a').text().trim(); const link = 'https://getdaytrends.com' + $(el).find('.main a').attr('href'); const volume = $(el).find('.desc').text().trim() || 'N/A'; if (name) { trends.push({ rank: i + 1, name, volume: volume.replace('Under ', '<'), link }); } }); ``` ```js slicedTrends.forEach((t) => { const rank = t.rank.toString().padStart(2, ' '); const name = t.name.length > 28 ? t.name.substring(0, 27) + '…' : t.name.padEnd(30); const volume = t.volume === 'N/A' ? chalk.gray(t.volume) : chalk.cyan(t.volume); console.log(`${chalk.gray(rank + '.')} ${chalk.white.bold(name)} ${volume.padStart(15)}`); }); ``` ### Technical Analysis The application trusts an external aggregator and buffers its entire HTTP response in a string without enforcing a maximum response size. It also does not configure ...[truncated 2126 chars]
- Remediation
- ## Remediation Suggestions 1. Enforce a maximum response-body size and abort the request once the limit is exceeded. 2. Configure connection and socket timeouts using `req.setTimeout()` or an equivalent abort mechanism. 3. Validate that the response `Content-Type` is an expected HTML media type before parsing it. 4. Stop consuming or explicitly destroy responses rejected because of their HTTP status. 5. Strip ANSI escape sequences and nonessential control characters from all remotely sourced values before terminal output. 6. Apply explicit length limits to parsed names, volumes, links, and the number of table rows. 7. Validate extracted links with `new URL()` and require the expected HTTPS origin before including them in JSON output. 8. Handle aborted and premature response termination events so partial responses are not treated as successful. 9. Add tests covering oversized responses, stalled connections, malformed HTML, and control characters in remote fields. A hardened response handler should track accumulated bytes rather than relying only on string length, destroy the request when the configured limit is exceeded, and sanitize each remote field immediately after extraction.
