T09 · Insecure Skill Coding Practices
Error
- Location
- src/converter.js:219
- Finding
- Unrestricted Blog URL Fetching Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `src/converter.js:219-242` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```javascript async fetchBlogContent(url) { try { const response = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' }, timeout: 10000 }); const $ = cheerio.load(response.data); // Remove scripts, styles, nav, footer $('script, style, nav, footer, header, aside').remove(); // Try to find main content let content = $('article').text() || $('main').text() || $('.post-content').text() || $('.entry-content').text() || $('body').text(); // Clean up whitespace return content.replace(/\s+/g, ' ').trim(); } catch (error) { throw new Error(`Failed to fetch blog content: ${error.message}`); } } ``` The vulnerable method is reached from user-controlled CLI input at `bin/cli.js:77-80` and `bin/cli.js:121-124`: ```javascript if (source.startsWith('http')) { console.log('📥 Fetching blog content...'); content = await converter.fetchBlogContent(source); } ``` ### Technical Analysis The Skill sends an HTTP request to a user-supplied URL without validating: - The URL scheme. - The destination hostname and resolved IP addresses. - Whether the destination belongs to a loopback, private, link-local, multicast, or reserved network. - The destination port. - Redirect destinations. - The maximum response size. The superficial `source.startsWith('http')` check does not provide a security boundary. Values beginning with `http://` or `https://` can still target internal services, such as loopback interfaces, private network hosts, or link-local cloud metadata services. Axios follows redirects by default. Consequently, even validation of only the initial URL would be insufficient: an apparently public endpoint could redirect the request to an ...[truncated 1893 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse input with the standard `URL` class and allow only `http:` and `https:` protocols. 2. Resolve the hostname before connecting and reject every address in: - IPv4 and IPv6 loopback ranges. - RFC 1918 private ranges. - Link-local ranges. - Multicast, unspecified, documentation, and other reserved ranges. - IPv4-mapped IPv6 representations of prohibited IPv4 addresses. 3. Prevent DNS rebinding by ensuring the validated address is the address used for the connection. 4. Disable automatic redirects or validate the scheme, hostname, resolved addresses, and port at every redirect hop. 5. Apply an explicit allowlist of public domains when the operational use case permits it. 6. Restrict destination ports to expected web ports, such as 80 and 443, unless other ports are explicitly required. 7. Set a strict response-size limit using Axios `maxContentLength` and reject non-text content types. 8. Consider routing retrieval through an isolated fetch service with no access to internal networks or cloud metadata endpoints. 9. Inform users before fetched content is submitted to OpenAI, and provide a local-only or confirmation mode for sensitive material. 10. Add tests for loopback, private IPv4, IPv6, link-local, encoded IP forms, DNS rebinding, redirects to internal addresses, oversized responses, and unsupported schemes. ]]>
