T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/stt.sh:103
- Finding
- Arbitrary Python Code Execution Through Shell Variable Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/stt.sh:25-40, 82-84, 89-91, 103-137` **Vulnerability Type**: Python code injection through unsafe string interpolation **Risk Level**: High ### Vulnerable Code ```bash # Parse arguments while [[ $# -gt 0 ]]; do case $1 in -o|--output) OUTPUT_FILE="$2" shift 2 ;; -m|--model) MODEL="$2" shift 2 ;; -f|--format) FORMAT="$2" shift 2 ;; ``` ```bash # Find the downloaded file AUDIO_FILE=$(ls -t "$AUDIO_DIR" | head -1) AUDIO_PATH="$AUDIO_DIR/$AUDIO_FILE" ``` ```bash python3 -c " import whisper import json model = whisper.load_model('$MODEL') result = model.transcribe('$AUDIO_PATH') text = result['text'] # Save based on format if '$FORMAT' == 'json': with open('$OUTPUT_FILE', 'w') as f: json.dump(result, f, indent=2) elif '$FORMAT' == 'srt': # Generate SRT with open('$OUTPUT_FILE', 'w') as f: for i, segment in enumerate(result['segments'], 1): start = segment['start'] end = segment['end'] content = segment['text'] f.write(f'$i\\n') f.write(f'{int(start//3600):02d}:{int((start%3600)//60):02d},{int((start%1)*1000):03d} --> ') f.write(f'{int(end//3600):02d}:{int((end%3600)//60):02d},{int((end%1)*1000):03d}\\n') f.write(f'{content}\\n\\n') else: with open('$OUTPUT_FILE', 'w') as f: f.write(text) print(f'Transcription saved to: $OUTPUT_FILE') print(f'Text: {text[:200]}...') " ``` ### Technical Analysis The script constructs an entire Python program inside a double-quoted shell string and directly interpolates `MODEL`, `FORMAT`, `OUTPUT_FILE`, and `AUDIO_PATH` into single-quoted Python string literals. The shell parser does not validate the option values against the documented model and format choices. Consequently, an input containing a single quot ...[truncated 2515 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the dynamically generated `python3 -c` program. Place the transcription implementation in a normal Python module and pass all values as command-line arguments: ```bash python3 "$SCRIPT_DIR/stt.py" \ --model "$MODEL" \ --format "$FORMAT" \ --output "$OUTPUT_FILE" \ "$VIDEO_URL" ``` 2. Parse values with `argparse` in Python so arguments remain data and are never interpreted as source code. 3. Enforce strict allowlists in the shell wrapper before invoking Python: ```bash case "$MODEL" in tiny|base|small|medium|large) ;; *) echo "Invalid model" >&2; exit 2 ;; esac case "$FORMAT" in txt|srt|vtt|json) ;; *) echo "Invalid format" >&2; exit 2 ;; esac ``` 4. Validate that options requiring a value actually have a following argument before reading `$2`. 5. Obtain the downloaded filename deterministically from `yt-dlp`, such as by using its printed post-processing path, instead of selecting the newest arbitrary entry with `ls`. 6. Keep generated audio in a per-run directory created with `mktemp -d`, apply restrictive permissions, and remove it after transcription. 7. If arbitrary output paths are unnecessary, constrain output to the designated output directory and reject paths that resolve outside it. ]]>
