T09 · Insecure Skill Coding Practices
- Location
pipeline.py:263- Finding
Undisclosed Transmission of Story Content to a Hardcoded Third-Party TTS Service
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its stated video-generation purpose, but it needs review because it can execute unsafe shell commands from media filenames and sends story text to a hardcoded third-party TTS service without clear disclosure.
Review before installing. Use this only in an isolated workspace with trusted media files, do not place confidential text in output.txt, and assume story text may be sent both to the configured OpenAI-compatible endpoint and to the hardcoded TTS service. The package should be fixed to remove shell=True, validate media filenames, make the TTS endpoint configurable and disclosed, and require an explicit preflight confirmation before running the full pipeline.
pipeline.py:263Undisclosed Transmission of Story Content to a Hardcoded Third-Party TTS Service
pipeline.py:173Arbitrary Command Execution Through Shell-Interpreted Video Filenames
Using subprocess.run with shell=True on dynamically composed command text is a classic tool-parameter abuse issue. In this pipeline, ffmpeg commands are assembled from runtime state and local file references, so a malicious filename or unexpected input can alter command semantics and execute arbitrary shell commands.
def _run_cmd(cmd: str):
log(f" $ {cmd}")
subprocess.run(cmd, shell=True, check=True)
def _get_duration(file: str) -> float:
subprocess.check_output with shell=True on a command string that embeds a file path allows attacker-controlled media names to abuse shell parsing. Because the skill consumes files from local directories, an attacker who can place or influence file names can potentially achieve arbitrary command execution.
def _get_duration(file: str) -> float:
cmd = f'ffprobe -v error -show_entries format=duration -of csv=p=0 "{file}"'
return float(subprocess.check_output(cmd, shell=True).decode().strip())
# ─────────────────────────────────────────────────────────────
The skill describes capabilities that include environment-variable access, file read/write, network use, and shell execution, but it does not declare any explicit tool scope or permission boundaries. That omission increases the chance an agent will invoke powerful actions without user awareness or platform-level restriction, especially because the skill later instructs full pipeline execution.
The trigger phrases are broad and natural-language, making accidental or overly eager activation more likely. In this skill, activation can lead to shell commands, file creation, and external API transmission, so loose triggering materially raises the risk of unintended execution.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
brew install ffmpeg
sudo apt install ffmpeg
The configuration explicitly supports sending prompts and generated story content to an external OpenAI-compatible endpoint. External transmission is expected for this workflow, but it is still security-relevant because user input and generated content leave the local environment and the skill does not pair this with strong disclosure or destination restrictions.
cfg = {
# ── LLM ──────────────────────────────────
"api_key": "YOUR_OPENAI_COMPATIBLE_API_KEY",
"base_url": "https://api.example.com/v1", # OpenAI 兼容端点
"model": "gpt-4o",
# ── Prompt(可选,有内置默认值)──────────
The environment-variable example reinforces use of an external API endpoint and encourages execution without emphasizing what data will be transmitted. This is not inherently malicious, but it contributes to a workflow where sensitive prompts or text could be sent off-device without adequate warning.
export OPENAI_API_KEY="sk-xxx"
export OPENAI_BASE_URL="https://api.example.com/v1"
export OPENAI_MODEL="gpt-4o"
python pipeline.py --all --theme 修仙
The Python invocation example again normalizes outbound transmission of generated content to an external service. In context, this is functional rather than covert, but it remains a true security concern because the skill lacks explicit safeguards around endpoint trust, user consent, and sensitive-content handling.
cfg = {
"api_key": "sk-xxx",
"base_url": "https://api.example.com/v1",
"model": "gpt-4o",
}
The skill instructs the agent to run python pipeline.py --all --theme <题材> directly and stream logs, but it does not require warning the user that local files will be created and that content may be sent to an external LLM API. This can cause unintended data transmission and code execution in the user's environment without meaningful informed consent.
The operational instructions are written as a fixed behavior for handling Chinese-language requests and examples, but they do not offer the user a language or locale choice. This can amount to a language-policy issue when a skill implicitly constrains responses or workflow to a specific language without opt-in or justification.
The prompts require Chinese-language generation, and the TTS configuration fixes a zh-CN voice, effectively forcing a specific language/locale. The file does not offer opt-in language selection or explain a necessary region-specific constraint.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# 4. ffmpeg / ffprobe
for tool in ("ffmpeg", "ffprobe"):
try:
subprocess.run([tool, "-version"], capture_output=True, check=True)
except (FileNotFoundError, subprocess.CalledProcessError):
errors.append(
f" ✗ 系统未找到 {tool}\n"
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
f" ✗ 系统未找到 {tool}\n"
f" → 请安装 FFmpeg:https://ffmpeg.org/download.html\n"
f" macOS: brew install ffmpeg\n"
f" Ubuntu: sudo apt install ffmpeg\n"
f" Windows: 下载后将 bin/ 目录加入 PATH"
)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def _make_client(cfg: dict) -> OpenAI:
api_key = cfg.get("api_key") or os.environ.get("OPENAI_API_KEY", "")
base_url = cfg.get("base_url") or os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1")
if not api_key:
raise ValueError("缺少 API Key:请在 cfg['api_key'] 或环境变量 OPENAI_API_KEY 中提供")
return OpenAI(api_key=api_key, base_url=base_url)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def _make_client(cfg: dict) -> OpenAI:
api_key = cfg.get("api_key") or os.environ.get("OPENAI_API_KEY", "")
base_url = cfg.get("base_url") or os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1")
if not api_key:
raise ValueError("缺少 API Key:请在 cfg['api_key'] 或环境变量 OPENAI_API_KEY 中提供")
return OpenAI(api_key=api_key, base_url=base_url)
The shell execution capability in _run_cmd and _get_duration is broader than necessary for a video-generation pipeline and increases the attack surface substantially. Since this skill processes local media files and generated lists, unnecessary shell invocation creates a realistic path to host command execution from crafted inputs.
_run_cmd executes a caller-supplied command string with shell=True, which enables shell metacharacter injection if any part of the command incorporates untrusted filenames or parameters. In this skill, that risk is real because command strings are built from filesystem-derived values and runtime options before being passed to ffmpeg via this helper.
def _run_cmd(cmd: str):
log(f" $ {cmd}")
subprocess.run(cmd, shell=True, check=True)
def _get_duration(file: str) -> float:
_get_duration constructs a shell command containing a file path and executes it with shell=True. Because file names can contain shell-significant characters, a crafted media filename in audio/ or video/ could break out of the quoted context and trigger arbitrary command execution on the host.
def _get_duration(file: str) -> float:
cmd = f'ffprobe -v error -show_entries format=duration -of csv=p=0 "{file}"'
return float(subprocess.check_output(cmd, shell=True).decode().strip())
# ─────────────────────────────────────────────────────────────
The skill sends story text from output.txt to a third-party TTS endpoint without clear in-code consent, disclosure, or data-handling controls. If users place sensitive or proprietary text into the pipeline, that content is exfiltrated to an external service outside the local environment.
This requests.post call transmits generated text to an external TTS API. In the skill context, the content is user-influenced and may include sensitive material, so the outbound transfer is a real privacy and data-governance concern even if it is part of intended functionality.
def _call_tts(text: str, index: int):
payload = {"text": text, **VOICE_CONFIG}
r = requests.post(TTS_API_URL, json=payload, timeout=60)
if r.status_code == 200:
path = os.path.join(AUDIO_DIR, f"{index:03d}.mp3")
with open(path, "wb") as f:
No suspicious patterns detected.