T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_subtitle.py:139- Finding
Authentication Cookies Sent to Unvalidated Subtitle URLs
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill’s Bilibili subtitle workflow is mostly coherent, but it handles live account cookies in ways that need careful review before installation.
Review this skill before installing. Use it only if you are comfortable creating and storing Bilibili session cookies locally; keep cookie.txt out of shared folders and repositories, restrict its permissions, and consider using a separate low-risk Bilibili account. Avoid automatic use on incidental links, and prefer a fixed reviewed version with pinned dependencies.
scripts/fetch_subtitle.py:139Authentication Cookies Sent to Unvalidated Subtitle URLs
scripts/cookie_login.py:156Sensitive Session Cookies Stored Without Restrictive File Permissions
SKILL.md:14Unpinned Executable Dependencies and Installer Commands
声明描述的是一个覆盖登录、字幕提取、摘要和时间定位的完整 B 站视频理解 skill;但当前代码片段只包含 cookie_login.py,功能限于扫码登录并保存 Cookie。已实现的登录能力与声明中的第(1)点一致,但其余核心能力均未出现在代码中。因此,代码实际行为明显窄于声明的主要用途,构成描述与行为不匹配。未发现超出声明范围的可疑未声明能力;问题在于严重过度声明。
代码与描述部分重合:确实支持 B 站链接/BVID 输入、优先 AI 字幕并回退 CC 字幕、使用 Cookie 提升获取能力。但描述中的多个核心能力在代码中完全不存在。首先,没有扫码登录、浏览器交互、二维码生成或 Cookie 获取流程,只是读取本地 cookie.txt。其次,没有任何摘要算法、LLM 调用或文本总结逻辑。再次,也没有检索字幕内容并返回对应时间段的问答/定位功能,虽然会生成带时间戳的 srt/vtt,但并未实现查询定位。整体上,代码的实际用途是“字幕抓取与导出脚本”,明显窄于声明的“完整内容理解工作流”,因此属于描述与行为不一致。
声明描述的是一个面向 B 站视频内容理解的完整多步骤技能,而提供的代码片段只是其中很小的一部分:在已有 SRT 文件中搜索关键词并返回时间戳。这与声明中的主要能力和主要用途存在实质性差异。代码确实部分支持“从字幕中定位时间段”这一子能力,但前提是用户已经有本地 SRT 文件;它没有任何 B 站相关网络访问、身份登录、字幕抓取、AI/CC 回退、摘要生成或链接/BVID 处理逻辑。因此该描述不能准确代表此代码块的实际行为,应判定为 mismatch。
The trigger conditions are explicitly broad: the skill says it must activate for many generic Bilibili-related phrases and even for a casual mention of a bilibili link. Because this skill can lead to login guidance, cookie collection, network access, and local file writes, over-triggering increases the chance of invoking sensitive workflows without clear user intent. In context, that makes accidental credential-handling and unnecessary external requests more likely.
The README instructs users to run npx skills add BAIKEMARK/bilibili-summary-skill without pinning a specific version of the skills package or the installed skill artifact. Unpinned package execution can cause users to fetch and run a newer or compromised release than the one reviewed, creating a supply-chain risk that is especially relevant for an agent skill that handles login flows and local cookie files.
The skill directs the agent to perform file reads/writes and network-backed subtitle retrieval, but it declares no explicit tool or permission scope. That creates an authorization ambiguity where a host may permit broader actions than users expect, especially because the workflow includes writing cookie.txt and subtitle files locally. In a skill ecosystem, undeclared capabilities reduce transparency and weaken least-privilege controls.
The skill instructs users to obtain and store sensitive Bilibili session cookies locally, including manual extraction of SESSDATA and bili_jct, but it does not provide a strong warning about credential sensitivity, storage risks, or scope of access. These cookies may grant account access and can be abused if exposed through logs, shared directories, backups, or other tools. The context makes this more dangerous because the workflow normalizes credential collection as a routine step for subtitle access.
The script saves live Bilibili authentication material (SESSDATA and bili_jct) in plaintext on disk without strong warnings, permission hardening, or any storage protection. These cookies can often be reused to access the user's account session, so any local compromise, accidental sharing, backup sync, or multi-user system exposure could lead to account takeover or unauthorized actions.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"""尝试获取 AI 自动生成的字幕。"""
# 方法1: wbi/v2 接口
try:
url = f"https://api.bilibili.com/x/player/wbi/v2?bvid={bvid}&cid={cid}"
data = requests.get(url, headers=headers, timeout=10).json()
subtitles = data.get("data", {}).get("subtitle", {}).get("subtitles", [])
for sub in subtitles:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"""尝试获取 AI 自动生成的字幕。"""
# 方法1: wbi/v2 接口
try:
url = f"https://api.bilibili.com/x/player/wbi/v2?bvid={bvid}&cid={cid}"
data = requests.get(url, headers=headers, timeout=10).json()
subtitles = data.get("data", {}).get("subtitle", {}).get("subtitles", [])
for sub in subtitles:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"""尝试获取 AI 自动生成的字幕。"""
# 方法1: wbi/v2 接口
try:
url = f"https://api.bilibili.com/x/player/wbi/v2?bvid={bvid}&cid={cid}"
data = requests.get(url, headers=headers, timeout=10).json()
subtitles = data.get("data", {}).get("subtitle", {}).get("subtitles", [])
for sub in subtitles:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"""尝试获取 AI 自动生成的字幕。"""
# 方法1: wbi/v2 接口
try:
url = f"https://api.bilibili.com/x/player/wbi/v2?bvid={bvid}&cid={cid}"
data = requests.get(url, headers=headers, timeout=10).json()
subtitles = data.get("data", {}).get("subtitle", {}).get("subtitles", [])
for sub in subtitles:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"""尝试获取 AI 自动生成的字幕。"""
# 方法1: wbi/v2 接口
try:
url = f"https://api.bilibili.com/x/player/wbi/v2?bvid={bvid}&cid={cid}"
data = requests.get(url, headers=headers, timeout=10).json()
subtitles = data.get("data", {}).get("subtitle", {}).get("subtitles", [])
for sub in subtitles:
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# 方法1: wbi/v2 接口
try:
url = f"https://api.bilibili.com/x/player/wbi/v2?bvid={bvid}&cid={cid}"
data = requests.get(url, headers=headers, timeout=10).json()
subtitles = data.get("data", {}).get("subtitle", {}).get("subtitles", [])
for sub in subtitles:
if sub.get("type") == 1:
Here the code follows subtitle_url returned by a remote API and reuses authenticated headers, including the user's Cookie, for the second request without validating the destination. If Bilibili or a man-in-the-middle/compromised upstream returned an attacker-controlled URL, the script could leak the user's Bilibili cookie to another host or be used for limited SSRF-style outbound requests.
continue
if sub_url.startswith("//"):
sub_url = "https:" + sub_url
body = requests.get(sub_url, headers=headers, timeout=10).json()
if "body" in body:
return body["body"]
except Exception:
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# 方法2: web-interface/view 接口
try:
url = f"https://api.bilibili.com/x/web-interface/view?bvid={bvid}"
resp = requests.get(url, headers=headers, timeout=10).json()
if resp.get("code") == 0:
subs = resp.get("data", {}).get("subtitle", {}).get("subtitles", [])
if subs:
This line makes a second-stage request to a URL obtained from remote response data, again with the same headers that may contain authentication cookies. That creates a real risk of credential leakage or server-side fetching of unexpected endpoints if the returned subtitle URL is malicious or outside the expected Bilibili domain set.
if sub_url.startswith("//"):
sub_url = "https:" + sub_url
if sub_url:
body = requests.get(sub_url, headers=headers, timeout=10).json()
if "body" in body:
return body["body"]
except Exception:
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
if not body:
print("🔍 正在查找 CC 字幕...")
url = f"https://api.bilibili.com/x/player/v2?bvid={bvid}&cid={cid}"
info = requests.get(url, headers=headers, timeout=10).json()
subs = info.get("data", {}).get("subtitle", {}).get("subtitles", [])
if not subs:
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
if not sub_url:
try:
fb_url = f"https://api.bilibili.com/x/player/wbi/v2?bvid={bvid}&cid={cid}"
fb = requests.get(fb_url, headers=headers, timeout=10).json()
fb_subs = fb.get("data", {}).get("subtitle", {}).get("subtitles", [])
if fb_subs:
sub_url = fb_subs[0].get("subtitle_url") or fb_subs[0].get("subtitle_url_v2", "")
This is the clearest instance of the issue: a subtitle URL from remote API data is fetched directly with headers that may include the user's Cookie. In the context of a skill that explicitly instructs users to log in and store cookies, this increases risk because credential-bearing requests may be sent to untrusted destinations if response data is ever tampered with.
sub_url = "https:" + sub_url
print("⬇️ 正在下载字幕...")
body = requests.get(sub_url, headers=headers, timeout=10).json().get("body")
if not body:
return None, "❌ 获取字幕失败:返回数据为空"
Natural-language strings throughout the file, including usage instructions and user-facing prompts, are presented only in Chinese. This can constitute a language policy violation when the skill forces a specific language without giving the user an explicit choice or documenting a justified locale restriction.
该文件的模块说明、CLI 帮助文本和运行时提示均固定为中文,未提供用户可选的语言或 locale 选项。根据规则,强制特定语言而没有用户 opt-in 属于自然语言层面的策略问题。
This code file contains natural-language strings that force a specific language/locale for usage instructions and output. Under the policy, language constraints should offer user choice or be clearly justified; here no opt-in or locale justification is provided.
No suspicious patterns detected.