T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- scripts/utils/validator.js:12
- Finding
- Arbitrary Local File Upload Through Unrestricted File Input<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:12-19`, `scripts/video2text/index.js:123-146`, `scripts/utils/upload.js:10-43` **Vulnerability Type**: Unrestricted local file access and external upload **Risk Level**: High ### Vulnerable Code ```js // scripts/utils/validator.js:12-19 function isFilePath(path) { try { const stats = fs.statSync(path); return stats.isFile(); } catch (_) { return false; } } ``` ```js // scripts/video2text/index.js:123-146 if (!fs.existsSync(file)) { utils.printError("文件不存在: " + file); process.exit(1); } try { 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/upload.js:10-43 async function uploadFileToOSS(filename, presignedUrl, headers) { const url = new URL(presignedUrl); if (url.protocol !== "https:") { throw new Error("上传URL必须是HTTPS协议"); } return new Promise((resolve, reject) => { const fileStats = fs.statSync(filename); const totalSize = fileStats.size; let uploadedSize = 0; let settled = false; const fileStream = fs.createReadStream(filename); const uploadHeaders = Object.assign({}, headers); uploadHeaders["Content-Length"] = String(totalSize); if (!uploadHeaders["Content-Type"]) { uploadHeaders["Content-Type"] = "application/octet-stream"; } const options = { host: url.hostname, path: url.pathname + url.search, method: "PUT", headers: uploadHeaders, }; const req = https.request( { ...options, timeout: constants.REQUEST_TIMEOUT }, (res) => { ``` ### Technical Analysis The local-file validator only checks whether the supplied path refers to a r ...[truncated 1864 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require explicit user confirmation before uploading any local file. 2. Restrict local inputs to user-approved directories or to files explicitly attached to the current task. 3. Resolve the canonical path with `fs.realpath()` and verify that it remains under an approved root. 4. Reject symbolic links or verify both the link and final target before opening the file. 5. Allow only supported media extensions, but do not rely on extensions alone. 6. Inspect file magic bytes and compare them against an allowlist of supported video and audio formats. 7. Reject non-media MIME types and unsupported container formats. 8. Enforce a maximum file size before opening the stream. 9. Open the validated file safely and protect against validation-to-use path replacement where the threat model includes local attackers. 10. Send only a sanitized basename to the presign API rather than the complete local path. ]]>
