T09 · Insecure Skill Coding Practices
Error
- Location
- lib/parser.js:143
- Finding
- Unrestricted URL Navigation and Download Enables SSRF-Like Access<![CDATA[ ## Vulnerability Details **File Location**: `lib/parser.js:143-148`, `lib/parser.js:95`, `lib/downloader.js:106-124` **Vulnerability Type**: Missing URL and destination validation **Risk Level**: High ### Complete Code Snippet ```js // lib/parser.js:143-148 async function parseDouyinUrl(inputUrl) { try { let targetUrl = inputUrl; if (inputUrl.includes('v.douyin.com')) { targetUrl = await resolveShortUrl(inputUrl); } const result = await fetchVideoInfo(targetUrl); ``` ```js // lib/parser.js:95 await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }); ``` ```js // lib/downloader.js:106-124 async function downloadVideo(videoUrl, outputDir, videoId, options = {}) { ensureDir(outputDir); const filename = options.filename || `${videoId}.mp4`; const filePath = path.join(outputDir, filename); console.log(` 🚀 正在尝试无水印解析下载...`); // 核心去水印链接构造 (严格白名单过滤) let downloadUrl = videoUrl; if (videoId && !videoUrl.includes('video_id=')) { // 仅允许字母、数字和下划线的 video_id,防止非法构造 if (/^[a-z0-9A-Z_]+$/.test(videoId)) { downloadUrl = `https://aweme.snssdk.com/aweme/v1/play/?video_id=${videoId}&ratio=1080p&line=0`; } } // 确保是 play 而不是 playwm downloadUrl = downloadUrl.replace('playwm', 'play'); try { const result = await downloadWithCurl(downloadUrl, filePath); ``` ### Technical Analysis The CLI accepts an arbitrary string as the target URL. The parser only checks whether the input contains the substring `v.douyin.com`; it does not parse the URL or enforce an exact hostname allowlist. For all other inputs, the supplied URL is passed directly to Playwright's `page.goto()`. This permits requests to arbitrary public or private HTTP services, including loopback, private-network, and link-local addresses. If parsing does not recover a valid video identifier, the original target URL can also remain the downloader's `downloadUrl`. The downloader then invokes `curl` with `-L`, allowing redire ...[truncated 1589 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse every input with the standard `URL` class before performing any request. 2. Permit only `https:` URLs. 3. Enforce exact or boundary-aware hostnames, such as: - `douyin.com` - `www.douyin.com` - `v.douyin.com` - Explicitly required Douyin media domains 4. Do not use substring checks such as `includes('v.douyin.com')`, because domains such as `v.douyin.com.attacker.example` would pass. 5. Resolve hostnames and reject loopback, private, link-local, multicast, and reserved IP ranges for both IPv4 and IPv6. 6. Revalidate every redirect destination rather than allowing unrestricted browser or `curl -L` redirects. 7. Never pass the original URL to the media downloader when parsing fails. Abort unless a validated media URL was produced. 8. Add tests for malformed URLs, user-info hostname tricks, alternate IP encodings, DNS rebinding, and redirects to private addresses. ]]>
