T09 · Insecure Skill Coding Practices
- Location
scripts/upload-image.ts:21- Finding
Unrestricted Local File Upload to External Hosting Services
- Content
View full analysis
{ const formData = new FormData(); const file = fs.readFileSync(filePath); const blob = new Blob([file]); formData.append('file', blob, path.basename(filePath)); const response = await fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', { method: 'POST', headers: { Authorization: `Bearer ${pinataJwt}`, }, body: formData, }); if (!response.ok) { throw new Error(`Pinata upload failed: ${response.statusText}`); } const data = (await response.json()) as PinataResponse; return `https://gateway.pinata.cloud/ipfs/${data.IpfsHash}`; } ``` Equivalent unrestricted reads and uploads also occur in the Cloudinary implementation at `scripts/upload-image.ts:56-58` and the imgBB implementation at `scripts/upload-image.ts:95-100`. ### Technical Analysis The image upload function accepts an arbitrary local path and reads its complete contents using `fs.readFileSync`. It does not verify that the target is a regular image file, inspect its actual MIME signature, enforce an extension allowlist, restrict access to an approved directory, or apply the documented five-megabyte size limit. Uploading a user-selected image to an external provider is necessary for the declared token-launching functionality. Reading and transmitting any file accessible to the process is broader than the minimum privilege required. The checks at `scripts/upload-image.ts:143-149` only verify that the path exists; they do not establish that the path refers to an acceptable image. The same issue affects all three supported providers. Pinata uploads the raw bytes, while Cloudinary and imgBB convert the bytes to Base64 before transmission. Merely labeli ...[truncated 1279 chars]- Remediation
View remediation
