T09 · Insecure Skill Coding Practices
- Location
scripts/video_gen.js:193- Finding
Arbitrary Local File Read and Upload Through Image Parameters
- Content
View full analysis
Vulnerability Details
File Location:
scripts/video_gen.js:193-266
Vulnerability Type: Unrestricted local file upload and sensitive data exposure
Risk Level: MediumVulnerable Code
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, }); } catch (err) { clearTimeout(timer); if (err.name === 'AbortError') { throw new Error(`Upload timeout: ${filePath}`); } throw err; } clearTimeout(timer); let data; try { data = await res.json(); } catch { throw new Error(`Upload failed with non-JSON response (HTTP ${res.status}).`); } const wrapped = { httpStatus: res.status, ...data }; if (!isApiSuccess(wrapped)) { const apiErr = formatApiError(wrapped); throw new Error(apiErr.errorMessage || `Upload failed (HTTP ${res.status}).`); } const uploadedUrl = ex ...[truncated 3979 chars]- Remediation
View remediation
Remediation Suggestions
-
Disable local paths by default. Require image inputs to be public HTTPS URLs unless the operator explicitly enables local upload with a flag such as
--allow-local-upload. -
Require explicit confirmation. Before reading a local file, display its canonical path, size, detected type, and upload destination. Require interactive approval or a separate machine-readable consent option.
-
Constrain filesystem access. Resolve the canonical path and require it to remain under an explicitly configured asset directory. Reject traversal outside that directory.
-
Restrict accepted formats. Permit only necessary image extensions such as
.jpg,.jpeg,.png,.webp, and.gif. Remove theapplication/octet-streamfallback for local image uploads. -
Validate file content. Inspect file signatures rather than trusting the extension. Reject files whose content does not match a supported image format.
-
Apply a file-size limit. Check
stat.sizebefore callingfs.readFile()and reject oversized files to reduce memory exhaustion and unintended large uploads. -
Handle symlinks safely. Use
lstat()and canonical-path checks to reject symbolic links or ensure their final target remains within the approved asset directory. -
Minimize publication exposure. If supported by WeryAI, request private or short-lived object URLs and document retention and deletion behavior. Avoid creating publicly accessible URLs for sensitive user assets.
-
Preserve documentation safeguards. Continue disclosing the destination host and credential use, but enforce the documented consent requirement in executable code rather than relying only on operator behavior.
-
