T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/video_gen.js:140
- Finding
- Unrestricted Local File Upload Without Enforced Consent or Image Validation## Vulnerability Details **File Location**: `scripts/video_gen.js:140-149, 206-251, 273-283, 378-384` **Vulnerability Type**: Arbitrary local file disclosure through automatic remote upload **Risk Level**: High ### Vulnerable Code ```js function normalizeLocalFilePath(value) { if (typeof value !== 'string' || !value.trim()) return null; if (value.startsWith('file://')) { return new URL(value); } return path.resolve(value); } function inferMimeType(filePath) { return IMAGE_MIME_TYPES[path.extname(filePath).toLowerCase()] || 'application/octet-stream'; } ``` ```js async function uploadFileToPublicUrl(inputPath, apiKey) { const resolvedPath = normalizeLocalFilePath(inputPath); if (!resolvedPath) { throw new Error(`Invalid local image path: ${inputPath}`); } const filePath = resolvedPath instanceof URL ? fileURLToPath(resolvedPath) : path.resolve(resolvedPath); let stat; try { stat = await fs.stat(filePath); } catch { throw new Error(`Local image file not found: ${inputPath}`); } if (!stat.isFile()) { throw new Error(`Local image path is not a file: ${inputPath}`); } const fileBuffer = await fs.readFile(filePath); const fileName = path.basename(filePath); const mimeType = inferMimeType(filePath); const form = new FormData(); form.append('batch_no', makeUploadBatchNo()); form.append('fixed', 'false'); form.append('file', new Blob([fileBuffer], { type: mimeType }), fileName); const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 60_000); let res; try { res = await fetch(MODELS_BASE_URL + UPLOAD_API_PATH, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, }, body: form, signal: controller.signal, }); ``` ```js async function ensurePublicImageUrl(value, apiKey) { if (typeof value !== 'string' || !value.trim()) { throw new Error('Image source must be a non-empty string.'); } if (isPubl ...[truncated 3917 chars]
- Remediation
- ## Remediation Suggestions 1. **Disable local paths by default.** Reject every image value that is not a valid public HTTPS URL during normal operation. 2. **Require an explicit capability flag.** Gate local uploads behind a clearly named option such as `--allow-local-upload`. The flag should not be inferred from the presence of a path. 3. **Enforce informed consent.** Before enabling the capability, show the exact resolved path, destination host, and warning that the service may return a public URL. For Agent-driven noninteractive use, require a separate trusted-layer authorization rather than treating JSON content as consent. 4. **Restrict filesystem scope.** Resolve canonical paths with `fs.realpath()` and require files to reside within an operator-approved upload directory. Reject path traversal, symlink escapes, device files, and paths outside that directory. 5. **Enforce actual image types.** Permit only required image formats and validate content using magic bytes or a trusted image decoder. Remove the `application/octet-stream` fallback and reject files whose extension and detected content disagree. 6. **Apply resource limits.** Check file size before reading, impose a conservative maximum upload size, and use streaming where supported to avoid loading an entire untrusted file into memory. 7. **Separate commands and privileges.** Consider moving local upload into a distinct command or helper that can run with narrower filesystem access. The primary generation commands should accept only HTTPS URLs. 8. **Improve dry-run warnings.** Display the canonical local path, expected destination, detected type, and size without reading or uploading file content. 9. **Test the security boundary.** Add automated tests confirming that sensitive paths, unsupported file types, symlink escapes, and local paths without the explicit authorization flag are rejected.
