T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/imap.js:329
- Finding
- Email Attachment Filename Allows Path Traversal and Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/imap.js:329-342` **Vulnerability Type**: Untrusted filename path traversal **Risk Level**: High ### Vulnerable Code ```js for (const attachment of parsed.attachments) { if (specificFilename && attachment.filename !== specificFilename) { continue; } if (attachment.content) { const filePath = path.join(outputDir, attachment.filename); fs.writeFileSync(filePath, attachment.content); downloaded.push({ filename: attachment.filename, path: filePath, size: attachment.size, }); } } ``` ### Technical Analysis The attachment filename originates from an email controlled by its sender. The code appends that untrusted filename directly to the user-selected output directory without sanitizing path separators, removing parent-directory components, or verifying the final resolved path. A filename such as `../../target-file` can cause `path.join()` to construct a path outside `outputDir`. The subsequent `fs.writeFileSync()` call creates or overwrites that destination using the attachment content. No exclusive-create option or overwrite confirmation is used. This violates the expectation that the `download` command only writes files beneath its specified output directory. ### Attack Path 1. An attacker sends an email containing an attachment with a filename containing traversal components, such as `../../home/user/.config/example`. 2. The email reaches the mailbox configured for the Skill. 3. The user or agent invokes: ```bash node scripts/imap.js download <uid> --dir <download-directory> ``` 4. `mailparser` exposes the attacker-provided attachment filename. 5. The Skill joins that filename to `outputDir` without containment validation. 6. `fs.writeFileSync()` writes attacker-controlled attachment data outside the intended directory. 7. If the resolved destination already exists and is writable, it is overwritten. ### Impact Assessment An attacker can ...[truncated 491 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat every attachment filename as untrusted. 2. Reduce the supplied name to a safe basename: ```js const safeName = path.basename(attachment.filename || 'attachment.bin'); ``` 3. Reject empty names, `.` and `..`, control characters, path separators, and platform-specific reserved names. 4. Resolve and validate the final destination: ```js const baseDir = path.resolve(outputDir); const destination = path.resolve(baseDir, safeName); if ( destination !== baseDir && !destination.startsWith(baseDir + path.sep) ) { throw new Error('Unsafe attachment filename'); } ``` 5. Avoid silently replacing existing files. Use an exclusive write such as: ```js fs.writeFileSync(destination, attachment.content, { flag: 'wx' }); ``` 6. Consider generating a server-side filename and retaining the original filename only as metadata. 7. Add tests covering `../`, nested traversal, Windows separators, encoded filenames, control characters, and existing destinations. ]]>
