T09 · Insecure Skill Coding Practices
- Location
scripts/full_workflow.py:166- Finding
Arbitrary Shell Command Injection Through Unvalidated Preference Values
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is a local video-editing workflow, but it builds shell commands from editable saved preferences, creating a real command-execution risk.
Review or patch the subprocess command construction before installing, especially if you might import or share preference files. Use it only in a contained workspace with trusted media and trusted config/user_preferences.json, and expect it to save local editing preferences and overwrite its fixed output video files.
scripts/full_workflow.py:166Arbitrary Shell Command Injection Through Unvalidated Preference Values
SKILL.md:28Unpinned and Unlocked Third-Party Dependency Installation
This is the core risky pattern: a generic command runner uses shell=True for all generated ffmpeg/ffprobe commands, magnifying the attack surface across the workflow. Several command components come from learned preferences and generated file lists, so hostile values can abuse shell parsing or break command boundaries.
"""运行命令并打印进度"""
if description:
print(f"🎬 {description}...")
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"❌ 失败:{result.stderr}")
return False
The shell is unnecessarily invoked for a simple ffprobe call. In this workflow context the immediate risk is moderated by the hard-coded audio path, but the coding pattern remains unsafe and could become exploitable when adapted to external input.
]
# 获取音频时长
result = subprocess.run(
f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{AUDIO_FILE}"',
shell=True, capture_output=True, text=True
)
This shell-based ffprobe invocation repeats the same unsafe command-execution pattern for the output file. While less directly attacker-controlled in the current script, it reinforces an insecure habit and leaves room for exploitation if output paths become configurable or influenced by external state.
# 显示文件信息
if OUTPUT_FILE.exists():
size = OUTPUT_FILE.stat().st_size / 1024 / 1024
result = subprocess.run(
f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{OUTPUT_FILE}"',
shell=True, capture_output=True, text=True
)
The README content, headings, and usage guidance are written in Chinese and do not offer an alternative language or indicate that the skill is intentionally region- or audience-specific. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation.
This markdown file states that the script automatically remembers settings and retains the most recent 20 adjustment records, but it does not warn users that their preferences and usage history will be stored on disk. Because this behavior affects user data and privacy, the skill description should explicitly disclose the persistence and retention behavior.
The skill does disclose its self-learning behavior in several sections, but the flagged area indicates the main description lacks a clear upfront warning that user preferences are automatically recorded and reused. That can mislead users about persistence and privacy expectations, especially because editing habits may reveal sensitive workflow patterns or file usage behavior.
The skill's natural-language interface and descriptive text are entirely in Chinese, with no indication that the user can choose another language or locale. Under the stated policy, a fixed language without user opt-in is a language/locale policy violation unless clearly justified.
The script imports preference-learning functions and immediately loads stored user preferences at startup, while the module header also advertises that it 'automatically learns user preferences.' Although this is partially documented in comments/docstrings, there is no runtime disclosure near startup that preference data is being read from or persisted for future runs, which affects user data handling.
The helper executes shell commands via subprocess.run(..., shell=True) using command strings assembled from paths and preference-derived values. If any interpolated value contains shell metacharacters or is attacker-controlled through files, preferences, or environment manipulation, this can lead to arbitrary command execution.
"""运行命令并打印进度"""
if description:
print(f"🎬 {description}...")
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"❌ 失败:{result.stderr}")
return False
The script creates and overwrites several files, including temporary lists, subtitle files, generated audio, and final video outputs. While progress messages appear after execution begins, there is no clear upfront warning that running the workflow will modify the filesystem and overwrite outputs via ffmpeg's '-y' option.
The function is named transcribe_audio, the workflow labels this step as 语音转录, and the module docstring advertises 语音转录字幕, implying subtitles are derived from the audio content. In reality, the code ignores the audio for transcription and writes a fixed list of subtitle strings, using the audio only to measure duration.
This ffprobe invocation uses shell=True with a formatted command string containing a filesystem path. Although AUDIO_FILE is hard-coded here, using the shell for simple argument passing is unsafe by default and becomes exploitable if the path is ever made configurable or derived from untrusted input.
]
# 获取音频时长
result = subprocess.run(
f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{AUDIO_FILE}"',
shell=True, capture_output=True, text=True
)
This ffprobe call again uses shell=True with a command string containing a path. The current output path is internally defined, but the pattern is dangerous because any future change that makes the path user-controlled could turn it into command injection.
# 显示文件信息
if OUTPUT_FILE.exists():
size = OUTPUT_FILE.stat().st_size / 1024 / 1024
result = subprocess.run(
f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{OUTPUT_FILE}"',
shell=True, capture_output=True, text=True
)
This Python file contains its top-level usage documentation entirely in Chinese, and the rest of the script continues that language choice in user-facing output. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified.
The interactive experience uses Chinese-only prompts and confirmations, which imposes a specific language on all users. The file does not present a language option or explain a region-specific requirement that would justify the restriction.
This code's user-facing description and CLI output are written exclusively in Chinese, and the file does not indicate that the skill is region-specific or that users can opt into another language. Under the language/locale policy, forcing a specific language without user choice is a natural-language policy concern.
This markdown file documents creation of output video files and a configuration reset command, but it does not warn users that running the workflow may overwrite existing outputs or that reset will modify stored preferences. For markdown files, SQP-2 applies when descriptions omit warnings about behaviors that could affect user data or system state.
The guide instructs users to run python3 scripts/manage_preferences.py reset and says it restores all settings to defaults, but it does not explicitly warn that previously learned preferences will be lost. For a user-data-affecting operation, the markdown should include a clearer warning about this overwrite/reset effect.