T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/voice_handler.py:32
- Finding
- Command and Python Code Injection Through an Attacker-Controlled Audio Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/voice_handler.py`, lines 32-49 **Vulnerability Type**: Shell command injection and dynamically generated Python code injection **Risk Level**: High ### Vulnerable Code ```python if audio_file.endswith('.ogg'): wav_file = tempfile.mktemp(suffix=".wav") cmd = f"ffmpeg -i '{audio_file}' -ar 16000 -ac 1 '{wav_file}' -y 2>/dev/null" subprocess.run(cmd, shell=True, check=True) audio_file = wav_file # Transcribe with faster-whisper cmd = [ sys.executable, "-c", """ from faster_whisper import WhisperModel import sys model = WhisperModel('%s', device='cpu', compute_type='int8') segments, info = model.transcribe('%s', beam_size=5) text = ' '.join(segment.text for segment in segments) print(json.dumps({'text': text, 'language': info.language, 'probability': info.language_probability})) """ % (self.stt_model, audio_file) ] result = subprocess.run(cmd, capture_output=True, text=True) ``` ### Technical Analysis The `audio_file` value is embedded into two executable contexts without escaping: 1. For OGG files, it is interpolated into a shell command passed to `subprocess.run(..., shell=True)`. Single quotes in the path can terminate the intended shell argument and introduce shell operators and arbitrary commands. 2. The resulting path is interpolated into source code supplied to `python -c`. A single quote can terminate the Python string literal and append arbitrary Python statements. Using single quotes around the shell argument does not make the operation safe when the interpolated value itself can contain a single quote. Likewise, passing the generated Python program as an argument array only protects the outer process invocation; it does not prevent injection into the dynamically constructed source code. ### Attack Path 1. An attacker causes the voice-processing entry point to receive an audio path containing shell or Python metacharacters. This requires influence over the path pas ...[truncated 1264 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Remove `shell=True` and pass ffmpeg arguments as a list: ```python subprocess.run( [ "ffmpeg", "-i", audio_file, "-ar", "16000", "-ac", "1", wav_file, "-y", ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) ``` - Do not generate Python source with string interpolation. Import and invoke `WhisperModel` directly in the current process: ```python from faster_whisper import WhisperModel model = WhisperModel( self.stt_model, device="cpu", compute_type="int8", ) segments, info = model.transcribe(audio_file, beam_size=5) text = " ".join(segment.text for segment in segments) ``` - Canonicalize input paths with `Path.resolve()` and enforce that they remain under an approved media directory. - Reject non-regular files, symbolic links, unexpected extensions, and paths exceeding reasonable length limits. - Run media processing under a dedicated unprivileged account with narrowly scoped filesystem access. - Add regression tests using filenames containing quotes, semicolons, command substitutions, newlines, and Python syntax. ]]>
