T09 · Insecure Skill Coding Practices
Warning
- Location
- src/commands/create-token.js:68
- Finding
- Arbitrary Local File Upload Through Unvalidated Logo Path<![CDATA[ ## Vulnerability Details **File Location**: `src/commands/create-token.js:68-95` **Vulnerability Type**: Unrestricted local file read and network upload **Risk Level**: Medium ### Vulnerable Code ```js const resolvedLogo = path.resolve(logoPath); if (!fs.existsSync(resolvedLogo)) { err(`Logo file not found: ${resolvedLogo}`, 'INVALID_ARG'); } (async () => { const wallet = getWallet(); const provider = wallet.provider; // 1. Upload metadata + logo const form = new FormData(); form.append('name', name); form.append('symbol', symbol); form.append('description', description); form.append('website', website); form.append('twitter', twitter); form.append('telegram', telegram); form.append('creator', wallet.address); form.append('category', category); form.append('logo', fs.createReadStream(resolvedLogo), { filename: path.basename(resolvedLogo), }); const uploadRes = await fetch(`${API_BASE}/token/create`, { method: 'POST', body: form, headers: form.getHeaders(), }); ``` ### Technical Analysis The documentation states that `--logo` must identify a PNG, JPEG, or WEBP image no larger than 5 MB. The implementation only verifies that the resolved path exists. It does not verify that the path is a regular file, enforce a size limit, inspect the file signature, restrict MIME types, or reject symbolic links and special files. Any accessible local path can consequently be opened with `fs.createReadStream()` and transmitted to `https://hodl.dance/api/token/create`. Although uploading a logo is necessary for token creation, unrestricted access to arbitrary local files exceeds the minimum filesystem privilege required by that functionality. This issue becomes exploitable when an attacker can influence command arguments, such as through an agent prompt, automation configuration, copied command, or untrusted workflow input. ### Attack Path 1. An attacker persuades an agent or us ...[truncated 1203 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Call `fs.statSync()` or `fs.promises.stat()` and require `isFile()` before opening the path. 2. Enforce the documented 5 MB maximum using the file's stat size before creating the stream. 3. Validate content using trusted file-signature detection rather than relying on the extension or caller-supplied MIME type. 4. Allow only PNG, JPEG, and WEBP signatures and assign the corresponding fixed MIME type. 5. Reject symbolic links, device files, named pipes, sockets, directories, and other special filesystem objects. 6. Consider restricting file selection to an explicitly approved working directory. 7. Display the resolved path, detected type, and size and require confirmation before uploading when the command is used interactively. 8. Apply stream and HTTP timeouts and abort the upload if the transmitted byte count exceeds the limit. 9. Perform all validation before constructing the wallet or making any network request. ]]>
