T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_douyin_video.py:92- Finding
Unrestricted User-Controlled URL Access Enables SSRF
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its transcription purpose, but its Douyin fetcher can make browser and HTTP requests to arbitrary user-supplied URLs without domain checks.
Review before installing. Only run the Douyin fetcher on links you trust, because the code does not enforce Douyin-only URLs or block private-network targets. Run setup in a low-privilege environment, expect large third-party downloads, and treat generated videos/transcripts and terminal logs as potentially sensitive local data.
scripts/fetch_douyin_video.py:92Unrestricted User-Controlled URL Access Enables SSRF
scripts/setup_env.py:267Unpinned Dependencies and Browser Artifacts Create Supply-Chain Exposure
该代码与声明中的“本地音视频转文字”部分基本一致,包括支持的媒体格式、Whisper medium 转录、输出文本/json、后续由 agent 整理等。但声明把技能整体描述为同时支持抖音链接内容提取与本地转录,并特别声称通过 Playwright 绕过抖音登录墙抓取视频;在当前提供的代码片段中,这些能力完全未出现。按评估标准,这属于声明覆盖了重要能力,而代码片段的实际主要行为仅是其中一个子功能,因此存在实质性描述不匹配。
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
手动修复:
# 删除疑似损坏的模型缓存
rm ~/.cache/whisper/medium.pt
# 下次运行 Whisper 时自动重新下载
The skill describes capabilities that include shell execution, local file read/write, environment access, and network fetching, but it does not declare any explicit tool scope or permission boundaries. This creates an over-privileged integration risk: an agent may invoke powerful actions without clear least-privilege constraints, increasing the chance of unintended file access, command execution, or network activity.
The description states the skill uses Whisper to transcribe Chinese locally, and later sections describe outputting Chinese text as the default behavior. This is a language/locale constraint presented as mandatory rather than optional, with no indication that the user can choose another language or opt in to Chinese-only handling.
The one-line positioning says media is transcribed into Chinese text and then formatted for output. Because this is framed as the standard behavior for all uses, it imposes a specific language/locale without documenting user choice or justified regional scope.
The parameter language="zh" is described as forcing Chinese (强制中文), which is a natural-language locale policy constraint. The document does not indicate that users may opt out or choose another language, so it appears to impose a specific language by default rather than offering a choice.
The script downloads the source video to disk and also persists transcript and JSON outputs, but it does not provide an explicit warning, consent step, or retention guidance before creating these local artifacts. In this skill context, the files may contain sensitive audio, speech content, or copyrighted material, so silent persistence increases privacy and data-handling risk if the host environment is shared or logs/backups are retained.
The script prints the full raw transcript to stdout between RAW_TRANSCRIPT_START/END markers. Stdout is often captured by agent logs, terminals, CI runners, or orchestration systems, so this can expose sensitive spoken content well beyond the intended recipient.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def run_cmd(cmd: list, **kwargs) -> subprocess.CompletedProcess:
"""运行命令,捕获输出,失败时打印诊断信息。"""
try:
return subprocess.run(cmd, capture_output=True, text=True, **kwargs)
except FileNotFoundError:
error(f"命令未找到: {cmd[0]}")
raise
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if sys.platform == "win32" and os.path.exists(os.path.expandvars(cmd)):
return cmd
try:
result = subprocess.run([cmd, "--version"], capture_output=True, text=True, timeout=5)
if result.returncode == 0:
return cmd
except (FileNotFoundError, subprocess.TimeoutExpired):
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def check_python_version(python_path: str) -> tuple[bool, str]:
"""检查 Python 版本兼容性。返回 (是否兼容, 版本描述)。"""
try:
result = subprocess.run(
[python_path, "-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')"],
capture_output=True, text=True, timeout=10
)
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
"venv_python": python_path,
"venv_dir": str(VENV_DIR.resolve()),
"mirror": mirror,
"created_at": __import__("datetime").datetime.now().isoformat(),
"platform": platform.platform(),
"python_version": sys.version.split()[0],
}
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
shutil.rmtree(VENV_DIR)
info(f"已删除旧的 venv: {VENV_DIR}")
result = subprocess.run(
[base_python, "-m", "venv", str(VENV_DIR)],
capture_output=True, text=True
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# 先升级 pip + setuptools
info("升级 pip 和 setuptools...")
subprocess.run(
venv_python.split() + ["-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel",
"-i", MIRRORS[mirror]["url"]] +
(["--trusted-host", MIRRORS[mirror]["url"].split("://")[1].split("/")[0]]
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
for step_name, pkgs in packages_ordered:
info(f"安装 {step_name}...")
cmd = install_base + pkgs
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
# 提取关键错误信息
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# 4. 安装 Chromium
print()
info("安装 Chromium 浏览器...")
chromium_result = subprocess.run(
[venv_python, "-m", "playwright", "install", "chromium"],
capture_output=True, text=True
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
print(f"ffmpeg: {imageio_ffmpeg.get_ffmpeg_exe()}")
"""
verify_result = subprocess.run(
[venv_python, "-c", verify_script],
capture_output=True, text=True, timeout=30
)
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
missing = []
for mod_name, display in core_deps:
try:
__import__(mod_name)
except ImportError as e:
missing.append(f" - {display}: {e}")
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
missing = []
for mod_name, display in core_deps:
try:
__import__(mod_name)
except ImportError as e:
missing.append(f" - {display}: {e}")
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if not os.path.exists(ffprobe_exe):
# ffprobe 不存在时用 ffmpeg 自带的信息探测
ffmpeg_exe = imageio_ffmpeg.get_ffmpeg_exe()
result = subprocess.run(
[ffmpeg_exe, "-i", media_path, "-f", "null", "-"],
capture_output=True, text=True, timeout=30,
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
return float(h) * 3600 + float(m) * 60 + float(s)
return 0
result = subprocess.run(
[ffprobe_exe, "-v", "quiet", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", media_path],
capture_output=True, text=True, timeout=30,
The module sets WHISPER_LANGUAGE = "zh" and a Chinese initial prompt by default, which biases transcription to Mandarin/Chinese without offering a user choice in this file. This is a natural-language policy concern because it imposes a specific language/locale rather than making it configurable or opt-in.
The file’s user-facing docstring and usage instructions are entirely in Chinese, while later console output is mixed Chinese and English, with no indication that the tool is intentionally region-specific or that users may choose another language. This creates a natural-language locale constraint that is not opt-in and is not justified in the file.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if not python:
return -1, "", "venv Python not found"
try:
r = subprocess.run(
[python, "-c", script],
capture_output=True, text=True, timeout=timeout
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
print(f" [FAIL] {msg}")
print(" --fix: auto-running setup_env.py ...")
setup_script = SKILL_DIR / "scripts" / "setup_env.py"
result = subprocess.run(
[sys.executable, str(setup_script), "--force"],
capture_output=False,
timeout=600,