T09 · Insecure Skill Coding Practices
Error
- Location
- '); process.exit(1); } const result = await upload_media({ file_path: file }); ``` ### Technical Analysis The media upload function accepts an arbitrary filesystem path and passes it directly to `readFileSync`. It does not: - Restrict access to a designated media directory. - Canonicalize the supplied path before enforcing a boundary. - Reject absolute paths or traversal sequences. - Reject symbolic links. - Verify that the target is a regular file. - Validate the actual file signature against the decl ...[truncated 2294 chars]:211
- Finding
- Arbitrary Local File Disclosure Through Media Upload<![CDATA[ ## Vulnerability Details **File Location**: `core.mjs:211-218, 229-233`; `mcp-server.mjs:105-113, 150-151`; `cli.mjs:134-137, 174-177` **Vulnerability Type**: Unrestricted local file read and network transmission **Risk Level**: High ### Vulnerable Code `core.mjs:211-218, 229-233`: ```javascript export async function upload_media({ file_path, media_data, media_type, alt_text } = {}) { if (!file_path && !media_data) throw new Error('file_path or media_data is required'); const client = await getClient(); let data = media_data; if (file_path && !data) { const buffer = readFileSync(file_path); data = buffer.toString('base64'); } // Detect media type from extension if not provided if (!media_type && file_path) { const ext = file_path.split('.').pop().toLowerCase(); const types = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', mp4: 'video/mp4', webp: 'image/webp' }; media_type = types[ext] || 'application/octet-stream'; } const body = { mediaData: data, mediaType: media_type, }; const result = await client.media.upload({ body }); ``` `mcp-server.mjs:105-113, 150-151`: ```javascript { name: 'x_upload_media', description: 'Upload media (image/video/gif) for use in tweets. Returns a media ID.', inputSchema: { type: 'object', properties: { file_path: { type: 'string', description: 'Path to the file to upload' }, media_type: { type: 'string', description: 'MIME type (auto-detected from extension if omitted)' }, }, required: ['file_path'], }, }, ``` ```javascript case 'x_upload_media': result = await upload_media(params); break; ``` `cli.mjs:134-137, 174-177`: ```javascript if (mediaFile) { const upload = await upload_media({ file_path: mediaFile }); if (upload.data?.id) mediaIds = [upload.data.id]; } ``` ```javascript case 'upload': { const file = args[1]; if (!file) { console.error('Usage: wip-x upload <file>'); process.ex ...[truncated 2684 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Use an explicit upload root** - Require an administrator-configured media directory. - Resolve both the upload root and requested file with `realpath`. - Verify that the resolved file remains strictly inside the approved root. 2. **Harden filesystem validation** - Reject absolute paths unless explicitly permitted. - Reject traversal outside the approved root. - Use `lstat` and `stat` to reject symbolic links and non-regular files. - Open files with protections against symlink races where supported. - Apply a conservative maximum file size before reading the content. 3. **Validate actual content** - Permit only documented image and video formats. - Check file magic bytes rather than trusting the extension or caller-provided MIME type. - Reject `application/octet-stream` instead of uploading unknown file types. 4. **Reduce Agent privileges** - Prefer opaque attachment handles or bytes supplied by a trusted host instead of arbitrary paths. - Require explicit user confirmation showing the canonical path, size, type, and destination before an MCP upload. - Run the MCP process with minimal filesystem permissions. 5. **Improve implementation behavior** - Stream supported uploads where possible rather than reading the entire file synchronously. - Log upload authorization decisions without logging file contents or credentials. - Add tests covering absolute paths, `../` traversal, symlinks, oversized files, and extension/signature mismatches. ]]>
