T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/utils/validator.js:3
- Finding
- Unrestricted URL Download Enables Server-Side Request Forgery and External Data Exfiltration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:3-6`, `scripts/video2text/index.js:97-106`, `scripts/video2text/index.js:140-149`, `scripts/utils/download.js:317-331`, `scripts/utils/upload.js:10-12`, `scripts/utils/upload.js:35-39` **Vulnerability Type**: Unrestricted URL fetching, redirect-based SSRF, and external upload of fetched content **Risk Level**: High ### Vulnerable Code ```js // scripts/utils/validator.js:3-6 function isUrl(url) { try { const parsedUrl = new URL(url); return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; } catch (_) { return false; } } ``` ```js // scripts/video2text/index.js:97-106 if (validator.isUrl(file)) { const filepath = utils.downloadPath(); try { await fs.promises.mkdir(filepath, { recursive: true }); } catch (error) { utils.printError("临时下载目录创建失败: " + (error.message || String(error))); process.exit(1); } try { const downloadResult = await helper.download(file, filepath); ``` ```js // scripts/video2text/index.js:140-149 const presignedUrl = await video.getPresignedUrl(tokenValue, file); if (!presignedUrl || !presignedUrl?.url || presignedUrl.url === "") { throw new Error("获取预签名URL失败,请反馈给开发者"); } utils.printInfo("上传文件到安全空间..."); await upload.uploadFileToOSS(file, presignedUrl.url, presignedUrl.headers); utils.printInfo("文件上传到安全空间成功,获取视频分析任务ID"); ``` ```js // scripts/utils/download.js:317-331 const req = this.__protocol.request(options, (response) => { if (this.__isRequireRedirect(response)) { redirectCount++; if (redirectCount > this.__opts.maxRedirects) { const err = new Error("Too many redirects"); this.__setState(this.__states.FAILED); this.emit("error", err); return reject(err); } const redirectedURL = /^https?:\/\//.test(response.headers.location) ? response.headers.location : new URL(response.headers.location, url).href; this.emit("redirected", redirectedUR ...[truncated 3199 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Permit only explicitly supported public video providers or direct-media hosts where operationally possible. 2. Resolve hostnames before connecting and reject all non-public IPv4 and IPv6 ranges, including: - Loopback addresses. - RFC1918 private addresses. - Link-local addresses. - Unique-local IPv6 addresses. - Multicast, unspecified, reserved, and documentation ranges. - Cloud metadata endpoints. 3. Repeat destination validation after every redirect. Do not trust only the original URL. 4. Protect against DNS rebinding by connecting to a previously validated resolved address while preserving the expected TLS hostname. 5. Restrict destination ports to an approved set, normally 80 and 443. 6. Enforce a strict maximum redirect count. 7. Require an approved video MIME type and validate file signatures before upload. 8. Enforce maximum response and file sizes while streaming; abort the request immediately when limits are exceeded. 9. Apply a total download deadline in addition to socket inactivity timeouts. 10. Do not upload a fetched resource until all source, size, and media validation has completed successfully. 11. Consider running the downloader in a network sandbox that cannot reach loopback, private networks, metadata services, or internal infrastructure. ]]>
