T09 · Insecure Skill Coding Practices
- Location
scripts/asr.py:156- Finding
API Key Duplicated into the Request Body as a User Identifier
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a legitimate Doubao transcription skill, but it needs review because it uploads recordings to Volcengine and unnecessarily copies the API key into the request body.
Install only if you are comfortable sending selected recordings or audio URLs to Volcengine/ByteDance for processing and with the associated billing. Protect the API key with restrictive file permissions or an environment secret, avoid passing it on the command line, and prefer a patched version that does not place the API key in user.uid.
scripts/asr.py:156API Key Duplicated into the Request Body as a User Identifier
SKILL.md:37Agent-Facing Setup Instructions Store the API Key Without Enforcing Restrictive Permissions
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
把 Key 存到配置文件(推荐,持久化):
mkdir -p ~/.config/doubao-asr
printf '{"api_key": "你的API Key"}\n' > ~/.config/doubao-asr/config.json
chmod 600 ~/.config/doubao-asr/config.json
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
把 Key 存到配置文件(推荐,持久化):
mkdir -p ~/.config/doubao-asr
printf '{"api_key": "你的API Key"}\n' > ~/.config/doubao-asr/config.json
chmod 600 ~/.config/doubao-asr/config.json
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
mkdir -p ~/.config/doubao-asr
printf '{"api_key": "你的API Key"}\n' > ~/.config/doubao-asr/config.json
chmod 600 ~/.config/doubao-asr/config.json
或使用环境变量:export DOUBAO_ASR_API_KEY="你的API Key",或每次调用 --api-key。
The skill documentation instructs use of shell, filesystem, environment variables, and outbound network access, but the manifest declares no explicit tool scope or permissions boundaries. That creates an over-privileged integration surface where an agent may invoke the skill without clear least-privilege constraints, increasing the chance of unintended file access, secret handling, or network exfiltration.
The manifest says to use the skill whenever the user wants to transcribe or generate text from audio/video, which is a broad trigger phrase that can cause over-invocation. In an agentic system, overly broad routing increases the chance that unrelated user content, sensitive local files, or private URLs get sent to an external ASR provider without sufficiently specific user intent.
This is a duplicate persistence issue on the same line: long-lived storage of an API credential in a predictable path increases secret exposure risk. In context, the skill uploads user audio to a third-party service, so compromise of the key could enable unauthorized API usage and billing abuse.
# 方式 A(推荐,持久):写入配置文件
mkdir -p ~/.config/doubao-asr
cat > ~/.config/doubao-asr/config.json <<'EOF'
{"api_key": "你的API Key"}
EOF
This is a duplicate persistence issue on the same line: long-lived storage of an API credential in a predictable path increases secret exposure risk. In context, the skill uploads user audio to a third-party service, so compromise of the key could enable unauthorized API usage and billing abuse.
# 方式 A(推荐,持久):写入配置文件
mkdir -p ~/.config/doubao-asr
cat > ~/.config/doubao-asr/config.json <<'EOF'
{"api_key": "你的API Key"}
EOF
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
脚本位于 scripts/asr.py(相对 skill 目录),用绝对路径调用:
# 假设 skill 已安装到 <你的 skills 目录>/doubao-asr/(如 ~/.pi/agent/skills/doubao-asr 或 ~/.claude/skills/doubao-asr)
ASR="<你的 skills 目录>/doubao-asr/scripts/asr.py"
PYTHONIOENCODING=utf-8 python3 "$ASR" /path/to/录音.m4a # 转文字
PYTHONIOENCODING=utf-8 python3 "$ASR" /path/to/会议.mp4 # 视频自动提取音频
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
脚本位于 scripts/asr.py(相对 skill 目录),用绝对路径调用:
# 假设 skill 已安装到 <你的 skills 目录>/doubao-asr/(如 ~/.pi/agent/skills/doubao-asr 或 ~/.claude/skills/doubao-asr)
ASR="<你的 skills 目录>/doubao-asr/scripts/asr.py"
PYTHONIOENCODING=utf-8 python3 "$ASR" /path/to/录音.m4a # 转文字
PYTHONIOENCODING=utf-8 python3 "$ASR" /path/to/会议.mp4 # 视频自动提取音频
The module docstring, CLI descriptions, status messages, and setup instructions are all presented exclusively in Chinese, with no indication that language selection is optional or that the skill is intentionally limited to a Chinese-speaking context. Under the policy, forcing a specific language without user opt-in is a natural-language locale violation.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def check_ffmpeg() -> bool:
try:
subprocess.run(["ffmpeg", "-version"], capture_output=True, timeout=10)
return True
except Exception:
return False
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def probe_duration(path: str) -> Optional[float]:
"""用 ffprobe 获取音频时长(秒),失败返回 None。"""
try:
r = subprocess.run(
["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "json", path],
capture_output=True, text=True, timeout=60,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
"-vn", "-ar", "16000", "-ac", "1", "-b:a", "64k",
dst,
]
r = subprocess.run(cmd, capture_output=True, text=True, timeout=1800)
if r.returncode != 0:
print(f"ffmpeg 转码失败:\n{r.stderr[-2000:]}", file=sys.stderr)
sys.exit(2)
The script transmits audio content, or a user-supplied public URL to audio content, to a third-party external API. In this skill context that behavior is expected, but it still creates a real data-exposure risk because potentially sensitive recordings, transcripts, and a user identifier are sent off-host without any explicit consent check, redaction step, or allowlist enforcement.
"request": {"model_name": "bigmodel"},
}
# 大文件 base64 上传可能较慢;2h 音频 flash 处理一般 1~3 分钟
r = requests.post(FLASH_URL, json=payload, headers=headers, timeout=900)
status_code = r.headers.get("X-Api-Status-Code", "")
message = r.headers.get("X-Api-Message", "")
logid = r.headers.get("X-Tt-Logid", "")
No suspicious patterns detected.