T09 · Insecure Skill Coding Practices
Error
- Location
- voice-clone.py:229
- Finding
- Shell Command Injection Through the User-Controlled Output Path<![CDATA[ ## Vulnerability Details **File Location**: `voice-clone.py`, lines 229–250 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```python if args.engine == "edge": # Map to Edge TTS voice name edge_voices = { "zh-xiaoxiao": "zh-CN-XiaoxiaoNeural", "zh-xiaoyi": "zh-CN-XiaoyiNeural", "zh-yunyang": "zh-CN-YunyangNeural", "zh-yunxi": "zh-CN-YunxiNeural", "en-jenny": "en-US-JennyNeural", "en-aria": "en-US-AriaNeural", "en-guy": "en-US-GuyNeural", "en-sonia": "en-GB-SoniaNeural", } voice = edge_voices.get(args.voice, "zh-CN-XiaoxiaoNeural") output_file = await edge_tts_speak( args.text, voice, args.rate, args.pitch, args.output ) # Other engine branches also pass args.output through as output_file. print(f"\n✅ 语音合成成功!") print(f"📁 输出文件: {output_file}") # Try to play the file if possible. try: os.system(f"xdg-open '{output_file}' >/dev/null 2>&1 &") except: pass ``` The output path originates from the unrestricted command-line option: ```python parser.add_argument( "-o", "--output", type=str, help="输出文件路径" ) ``` ### Technical Analysis The `--output` argument is controlled by the caller and is passed to the selected synthesis function as the output filename. These functions return that value as `output_file`. After synthesis succeeds, the application interpolates `output_file` directly into a command string passed to `os.system()`. `os.system()` invokes a shell. Wrapping the value in single quotes is not sufficient because a filename containing a single quote can terminate the quoted argument and introduce shell metacharacters. The code does not escape or validate the value before shell interpretation. The surrounding `try/except` does not prevent exploitation. `os.system()` generally reports command failure through its numeric return value rather than raising an exception, and any injected command has a ...[truncated 1696 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not invoke a shell to open the generated file. Pass arguments directly to the operating system: ```python import subprocess subprocess.Popen( ["xdg-open", output_file], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) ``` Additional hardening should include: 1. Resolve the output path with `Path.resolve()` and, if arbitrary output locations are unnecessary, require it to remain under an approved output directory. 2. Reject output paths containing null bytes and paths targeting unsupported file types. 3. Consider removing automatic playback or making it an explicit opt-in option. 4. Verify that the output is a regular file before opening it. 5. Replace the bare `except` with explicit exception handling and security-relevant error logging. 6. Add regression tests using filenames containing quotes, semicolons, command substitutions, spaces, and newline characters. ]]>
