T09 · Insecure Skill Coding Practices
- Location
scripts/tts-converter.js:55- Finding
Insecure Temporary Storage and Retention of Synthesized Speech Data
- Content
View full analysis
Vulnerability Details
File Location:
scripts/tts-converter.js:18-20, 55-71, 128-129, 155-171; retention behavior documented inSKILL.md:198
Vulnerability Type: Unsafe temporary-file handling and sensitive data retention
Risk Level: MediumVulnerable Code
javascript const DEFAULT_TIMEOUT_MS = 10000; 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 }); } } 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 await ensureTempDir(); const finalOutputPath = outputPath || generateTempPath('.mp3');javascript await tts.ttsPromise(filteredText, finalOutputPath); const stats = await fs.stat(finalOutputPath); console.log(`\n✓ Audio saved to: ${finalOutputPath}`); console.log(`✓ File size: ${stats.size} bytes`); if (saveSubtitles) { const subtitlePath = finalOutputPath.replace(/\.[^/.]+$/, '.json'); try { const subStats = await fs.stat(subtitlePath); console.log(`✓ Subtitles saved to: ${subtitlePath}`); console.log(`✓ Subtitle size: ${subStats.size} bytes`); } catch (e) { console.log(`ℹ No subtitles generated`); } }The documented retention behavior is:
markdown Files are not automatically deleted - the calling application (Clawdbot) should handle cleanup after use.Technical Analysis
Synthesized audio and optional subtitle data are stored under a fixed directory in the operating system's shared temporary area. The directory is created without an explicit restrictive permission mode, and the ...[truncated 2286 chars]
- Remediation
View remediation
Remediation Suggestions
- Create a private per-invocation temporary directory using
fs.mkdtemp()beneathos.tmpdir(). - Explicitly set the directory mode to
0700and generated file modes to0600. - Use
crypto.randomUUID()or cryptographically secure random bytes instead ofMath.random(). - Create output files atomically with exclusive-create semantics where supported, preventing reuse of attacker-controlled paths.
- Validate that the temporary directory is owned by the current user and is not a symbolic link before writing.
- Delete temporary MP3 and JSON files in a
finallyblock after successful delivery, unless the caller explicitly requests permanent storage. - Add bounded retention as a fallback, such as deleting stale files on startup and enforcing maximum age and directory size.
- Avoid logging complete sensitive paths in shared logs unless operationally necessary.
- For caller-supplied permanent output paths, document overwrite behavior and consider rejecting symbolic links or requiring an explicit overwrite option.
- Create a private per-invocation temporary directory using
