T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/rss.js:41
- Finding
- Arbitrary Feed URLs Enable Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rss.js:41-64`, with attacker-controlled input reaching the function at `scripts/rss.js:139` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```javascript // Simple HTTP(S) fetch function fetchUrl(url) { return new Promise((resolve, reject) => { const client = url.startsWith('https') ? https : http; const req = client.get(url, { headers: { 'User-Agent': 'Clawdbot-RSS/1.0' }, timeout: 10000 }, (res) => { if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { // Follow redirect return fetchUrl(res.headers.location).then(resolve).catch(reject); } if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode}`)); return; } let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => resolve(data)); }); req.on('error', reject); req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); }); }); } ``` The user-controlled URL reaches this function during feed validation: ```javascript const xml = await fetchUrl(url); ``` ### Technical Analysis The `add` command accepts a user-provided feed URL and passes it to `fetchUrl()` without validating its destination. Stored feed URLs are subsequently fetched by the `check` command as well. The implementation does not: - Reject loopback, private, link-local, or multicast IP addresses. - Prevent access to cloud instance metadata endpoints. - Resolve hostnames and validate all returned IP addresses. - Revalidate the destination after redirects. - Impose a maximum redirect count. - Restrict destinations to a trusted host allowlist. An attacker can therefore cause the process to issue HTTP requests to services reachable from the host running the Skill. A public URL can also redirect to an internal address, bypassing valid ...[truncated 2023 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse every input with `new URL()` and reject malformed URLs. 2. Allow only the exact `http:` and `https:` protocols. 3. Resolve the hostname before connecting and reject every address in prohibited ranges, including: - IPv4 and IPv6 loopback ranges. - RFC 1918 private IPv4 ranges. - IPv4 and IPv6 link-local ranges. - Unique-local IPv6 ranges. - Multicast, unspecified, reserved, and documentation ranges. 4. Repeat URL, hostname, and resolved-address validation after every redirect. 5. Limit redirects to a small fixed number, such as three to five. 6. Protect against DNS rebinding by connecting only to the validated resolved address while preserving the intended hostname for TLS and the `Host` header. 7. Consider an explicit allowlist of approved feed domains when arbitrary feed sources are unnecessary. 8. Enforce maximum response sizes and abort oversized downloads to reduce denial-of-service risk. 9. Apply outbound network restrictions at the container or operating-system level so the process cannot reach metadata services or sensitive internal networks. ]]>
