T09 · Insecure Skill Coding Practices
- Location
scripts/html_to_markdown.mjs:205- Finding
Unrestricted URL Fetching Enables Server-Side Request Forgery
- Content
View full analysis
controller.abort(), timeoutMs); try { const res = await fetch(url, { redirect: 'follow', signal: controller.signal }); if (!res.ok) throw new Error(`Fetch failed: ${res.status} ${res.statusText}`); return await res.text(); } finally { clearTimeout(timer); } } ``` The function is reached using user-controlled values in both single-URL and URL-list modes: ```js async function readInput(args) { if (args.file) return fs.readFile(args.file, 'utf8'); if (args.html) return args.html; if (args.url) return fetchWithTimeout(args.url, args.timeoutMs); throw new Error('No input'); } ``` ```js for (const u of urls) { const out = path.join(args.outputDir, fileNameFromUrl(u)); try { const raw = await fetchWithTimeout(u, args.timeoutMs); const res = await convertOne(raw, args, u); ``` ### Technical Analysis The `--url` argument and every entry in a `--url-list` file are passed directly to `fetch()`. The implementation does not validate the URL scheme, destination hostname, resolved IP address, destination port, or redirect destination. Although the documentation presents HTTPS URLs, the code does not enforce that restriction. The fetch operation also uses `redirect: 'follow'`, so an initially public URL can redirect to a loopback, private-network, link-local, or cloud metadata address. The timeout limits request duration but does not prevent unauthorized network access. The response body is returned as text, converted to Markdown, and written to an attacker-selected output location. ### Attack Path 1. An attacker supplies a URL through `--url` or pl ...[truncated 1431 chars]- Remediation
View remediation
