T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/rss-digest.js:152
- Finding
- Registry-Controlled Feed URLs Permit Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rss-digest.js:152-181` and `scripts/rss-digest.js:488-494` **Vulnerability Type**: Server-Side Request Forgery through unvalidated feed URLs **Risk Level**: High ### Vulnerable Code ```js function normalizeRssUrl(rawUrl) { const value = String(rawUrl || '').trim(); if (!value) return ''; if (/^https?:\/\//i.test(value)) return value; const rsshubBase = String(CONFIG.rsshubUrl || '').trim().replace(/\/+$/, ''); if (!rsshubBase) return ''; if (value.startsWith('/')) return rsshubBase + value; return rsshubBase + '/' + value; } async function fetchTextByUrl(url) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 20000); try { const res = await fetch(url, { method: 'GET', headers: { Accept: 'application/rss+xml, application/atom+xml, application/xml, text/xml;q=0.9, */*;q=0.8' }, signal: controller.signal }); clearTimeout(timeout); if (!res.ok) { throw new Error('HTTP ' + res.status + ' ' + (await res.text())); } return await res.text(); } catch (e) { clearTimeout(timeout); throw e; } } ``` ```js const fetchResults = await mapWithConcurrency(sources, 5, async source => { const feedUrl = normalizeRssUrl(source.rss_url); if (!feedUrl) { log('跳过无效 rss_url:' + (source.name || source.id || 'unknown')); return []; } try { const xml = await fetchTextByUrl(feedUrl); const parsed = parseRssOrAtom(xml, source); return parsed; } catch (e) { log('抓取失败:' + (source.name || source.id || 'unknown') + ' - ' + (e.message || e)); return []; } }); ``` ### Technical Analysis The feed registry is obtained from a remote service, and every `rss_url` supplied by that service is subsequently requested by the local process. The only validation applied to an absolute URL is whether it begins with `http://` or `https://`. The implementation does no ...[truncated 2259 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Maintain an explicit allowlist of approved external feed domains. 2. Treat the configured local RSSHub origin as a separate, narrowly scoped exception; require its scheme, hostname, and port to match exactly. 3. Resolve hostnames before connecting and reject all loopback, private, link-local, multicast, unspecified, documentation, and reserved IPv4 and IPv6 ranges. 4. Repeat destination validation after every DNS resolution and for every redirect hop. 5. Disable automatic redirects where possible, or implement a small redirect limit with destination revalidation. 6. Require HTTPS for external feeds. 7. Add connection, read, and total-request timeouts. 8. Stream responses and enforce a conservative maximum body size. 9. Consider routing external feed requests through a restricted egress proxy. 10. Validate registry data against a strict schema before using it. ]]>
