T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/post-media.js:23
- Finding
- Unrestricted Local File Selection for Remote Media Upload<![CDATA[ ## Vulnerability Details **File Location**: `scripts/post-media.js:23-38` **Vulnerability Type**: Arbitrary local file upload through an unrestricted path **Risk Level**: Medium ### Vulnerable Code ```js // Check if media file exists if (!fs.existsSync(mediaPath)) { console.error(`Error: Media file not found: ${mediaPath}`); process.exit(1); } const client = new TwitterApi({ appKey: apiKey, appSecret: apiSecret, accessToken: accessToken, accessSecret: accessSecret, }); try { // Upload media first console.log('Uploading media...'); const mediaId = await client.v1.uploadMedia(mediaPath); ``` The value of `mediaPath` is taken directly from the command line: ```js const text = process.argv[2]; const mediaPath = process.argv[3]; if (!text || !mediaPath) { console.error('Usage: node post-media.js "Your tweet text" "/path/to/image.jpg"'); process.exit(1); } postTweetWithMedia(text, mediaPath); ``` ### Technical Analysis The script accepts an arbitrary filesystem path and verifies only that the path exists. It does not: - Restrict files to a dedicated workspace or approved media directory. - Resolve and validate the canonical path. - reject symbolic links. - Verify that the path refers to a regular file. - Validate the file's MIME type or extension. - Enforce a file-size limit. - Require the user to confirm the canonical path before transmission. The supplied path is passed directly to `twitter-api-v2`, which reads the local file and uploads it to X. This exceeds the minimum filesystem access required for publishing a user-approved attachment because the script can attempt to access any path readable by its process. Successful disclosure depends on the selected file being accepted by the X media API. Nevertheless, private local images, videos, or other accepted media files could be exposed. ### Attack Path 1. An attacker-controlled prompt, untrusted workflow input, or mistaken Agent decision supplies a sensitive local path ...[truncated 1101 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create a dedicated media directory and reject paths outside it. 2. Resolve the canonical path with `fs.realpathSync()` and verify that it remains under the approved directory. 3. Use `fs.lstatSync()` and `fs.statSync()` to reject symbolic links and non-regular files. 4. Allowlist supported media MIME types and verify content using file signatures rather than relying only on extensions. 5. Enforce explicit file-size and dimension limits before upload. 6. Display the canonical path, detected media type, size, and a preview to the user. 7. Require explicit approval tied to the exact file hash and tweet content. 8. Run the Skill with filesystem permissions limited to its workspace and approved media directory. Example boundary validation: ```js const approvedRoot = fs.realpathSync(process.env.X_MEDIA_ROOT); const canonicalPath = fs.realpathSync(mediaPath); const relativePath = path.relative(approvedRoot, canonicalPath); if ( relativePath.startsWith('..') || path.isAbsolute(relativePath) || !fs.statSync(canonicalPath).isFile() ) { throw new Error('Media path is outside the approved media directory'); } ``` ]]>
