T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/tts-converter.js:19
- Finding
- Insecure Shared Temporary Storage for Synthesized Audio<![CDATA[ ## Vulnerability Details **File Location**: `scripts/tts-converter.js:19-20`, `scripts/tts-converter.js:49-60`, and `scripts/tts-converter.js:123-125` **Vulnerability Type**: Unsafe temporary directory and file handling **Risk Level**: Medium ### Vulnerable Code ```javascript const MAX_TEXT_LENGTH = 10000; const TEMP_DIR = path.join(os.tmpdir(), 'edge-tts-temp'); ``` ```javascript async function ensureTempDir() { try { await fs.access(TEMP_DIR); } catch (error) { await fs.mkdir(TEMP_DIR, { recursive: true }); } } /** * Generate unique temporary file path * @param {string} extension - File extension (e.g., '.mp3') * @returns {string} Temporary file path */ function generateTempPath(extension = '.mp3') { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); const filename = `tts_${timestamp}_${random}${extension}`; return path.join(TEMP_DIR, filename); } ``` ```javascript // Ensure temp directory exists and use temp file if no output path specified await ensureTempDir(); const finalOutputPath = outputPath || generateTempPath('.mp3'); ``` The absence of automatic cleanup is also explicitly documented in `SKILL.md:193-195`: ```markdown - **Temporary File Handling**: By default, audio files are saved to the system's temporary directory (`/tmp/edge-tts-temp/` on Unix, `C:\Users\<user>\AppData\Local\Temp\edge-tts-temp\` on Windows) with unique filenames (e.g., `tts_1234567890_abc123.mp3`). Files are not automatically deleted - the calling application (Clawdbot) should handle cleanup after use. ``` ### Technical Analysis The converter places synthesized audio in a fixed directory beneath the system-wide temporary directory. It does not: - Create a process- or user-private temporary directory. - Explicitly enforce owner-only directory permissions such as `0700`. - Verify that an existing directory is owned by the current user. - Verify that the directory or destination is not a symbolic lin ...[truncated 3234 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Create a private temporary directory** - Use `fs.mkdtemp()` beneath `os.tmpdir()` so each execution receives an unpredictable, isolated directory. - Explicitly set or verify directory permissions as `0700`. 2. **Use cryptographically secure names** - Prefer the random directory generated by `mkdtemp()`. - If an additional random filename is required, use `crypto.randomBytes()` or `crypto.randomUUID()` rather than `Math.random()`. 3. **Create files exclusively** - Open output files with exclusive creation semantics, such as `O_CREAT | O_EXCL`. - Enforce mode `0600`. - If the dependency requires a path rather than a file descriptor, validate that the destination does not already exist and use an isolated directory inaccessible to other users. 4. **Validate filesystem objects** - Use `lstat()` rather than only `access()` when validating existing paths. - Reject symbolic links and non-directory objects. - On supported systems, verify that the directory is owned by the effective user and is not writable by group or other users. 5. **Delete temporary artifacts** - Remove generated audio and subtitle files in a `finally` block after they have been delivered or copied to their intended destination. - Remove the private temporary directory recursively after processing. - If deferred delivery prevents immediate deletion, implement a bounded retention period and scheduled application-level cleanup. 6. **Protect caller-selected output paths** - Document that `--output` should point to a trusted directory. - For service integration, restrict output to an approved root and reject path traversal or symbolic-link destinations where user-controlled paths are possible. A safer design would resemble: ```javascript const crypto = require('crypto'); async function createPrivateTempOutput() { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'edge-tts-')); await fs.chmod(tempDir, 0o7 ...[truncated 671 chars]
