T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/utils/validator.js:3
- Finding
- Unrestricted URL Fetching Enables Server-Side Request Forgery and Internal Data Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:3-7`, `scripts/video2text/index.js:101-109`, `scripts/utils/download.js:482-493`, `scripts/video2text/index.js:138-151` **Vulnerability Type**: Server-Side Request Forgery (SSRF) with remote data exfiltration **Risk Level**: High ### Vulnerable Code `scripts/utils/validator.js:3-7` only verifies that the input uses HTTP or HTTPS: ```js function isUrl(url) { try { const parsedUrl = new URL(url); return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; } catch (_) { return false; } } ``` `scripts/video2text/index.js:101-109` passes the accepted URL directly to the downloader: ```js 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); ``` `scripts/utils/download.js:482-493` follows redirects without validating the new destination: ```js if (this.__isRequireRedirect(response)) { this.__redirectCount++; if (this.__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, this.url).href; this.__isRedirected = true; this.__initProtocol(redirectedURL); this.emit("redirected", redirectedURL, this.url); return this.__start(); } ``` `scripts/video2text/index.js:138-151` subsequently uploads the downloaded response to remote storage: ```js try { const presignedUrl = await video.getPresignedUrl(tokenValue, file); if (!presignedUrl || !presignedUrl?.url || presignedUrl.url === "") { t ...[truncated 2820 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Reject non-public destinations before opening a connection: - Resolve all hostname A and AAAA records. - Reject loopback, private, link-local, multicast, unspecified, reserved, and documentation ranges. - Explicitly block cloud metadata addresses, including `169.254.169.254` and relevant IPv6 equivalents. 2. Repeat the complete validation procedure for every redirect destination. 3. Prevent DNS rebinding by connecting to the already validated IP address while preserving the intended TLS server name and HTTP `Host` value. 4. Consider an allowlist of supported public video platforms and approved content-delivery domains. 5. Reject URLs containing embedded credentials unless they are explicitly required. 6. Apply the same controls to both HTTP and HTTPS destinations. 7. Where practical, run the downloader in a restricted network environment that cannot reach private networks, local control planes, or metadata services. 8. Add automated tests covering direct private IPs, encoded IP representations, IPv6 local addresses, public-to-private redirects, multi-record DNS responses, and DNS rebinding scenarios. ]]>
