T08 · Insecure Dependencies
- Location
SKILL.md:8- Finding
Unpinned Third-Party Dependencies Create a Supply-Chain Risk
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its Bilibili scraping purpose, but it has review-worthy risks around global browser-session closure, unsafe shared temporary storage, under-warned login cookie handling, and unpinned optional dependencies.
Install only if you are comfortable with a Bilibili scraper that can use authenticated browser state and write harvested public/user-linked data locally. Avoid placing real account cookies in the skill directory unless necessary, keep any cookie file private and out of version control, set `BI_DATA_DIR` to a user-private directory instead of relying on `/tmp`, and review the unpinned Python dependencies before enabling danmaku features.
SKILL.md:8Unpinned Third-Party Dependencies Create a Supply-Chain Risk
bili-fetch.py:388Automatic Recovery Terminates All Agent-Browser Sessions
paths.py:29Predictable Shared Temporary Directory Is Trusted Without Ownership or Symlink Validation
The boundary section states '长视频弹幕导出 → agent-browser skill 自己写 (未集成)', which says danmaku export is not integrated into this skill. However, multiple earlier sections document built-in 'danmaku' subcommands and '--danmaku' harvest support, so this is an active contradiction about implemented capability.
The README instructs users to place live authentication cookies such as SESSDATA and bili_jct into a local file, but it does not explicitly warn that these values are credentials that can grant account access or expose private account state. In a scraping skill context, normalizing manual cookie injection without security guidance increases the chance that users mishandle, commit, or share sensitive tokens.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
echo "buvid3=xxx; SESSDATA=xxx; bili_jct=xxx; ..." > data/cookies-raw.txt chmod 600 data/cookies-raw.txt
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
echo "buvid3=xxx; SESSDATA=xxx; bili_jct=xxx; ..." > data/cookies-raw.txt chmod 600 data/cookies-raw.txt
The skill advertises and documents capabilities that imply shell, file, environment, and network access, but it does not declare an explicit tool scope or permissions boundary. In an agent ecosystem, that omission increases the chance the skill is run with broader privileges than necessary, enabling unnecessary file access, credential exposure, or arbitrary outbound requests if the implementation is compromised or misused.
The manifest description states the skill '自动复用 buvid cookie + wbi 签名环境', implying use of a WBI signing environment. Later documentation explicitly says '本 skill 所有命令不直接调 wbi 接口' and '完全不需要算 w_rid', which contradicts that earlier claim rather than merely omitting detail.
The setup instructions tell users to copy sensitive Bilibili cookies including SESSDATA and bili_jct into a local file, but they do not clearly warn that these values are authentication credentials that can enable account access or abuse if leaked. In a scraping-oriented skill that stores data on disk and interacts with browser state, this materially raises the risk of credential theft, accidental commit, or unsafe sharing.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def run(cmd, timeout=30, check=False):
"""跑 agent-browser 命令"""
r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
if check and r.returncode != 0:
print(f" stdout: {r.stdout[:300]}")
print(f" stderr: {r.stderr[:300]}")
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def bvid_to_aid(bvid):
"""BV 号 → aid (通过 web-interface/view 拿)"""
js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid={bvid}', {{credentials:'include'}});
const j = await r.json();
return JSON.stringify({{code: j.code, aid: j?.data?.aid, title: j?.data?.title, msg: j.message}});
}})()"""
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.
})()"""
res = ab_eval(page_js)
d = res if isinstance(res, dict) else {}
plist = d.get('list', []) if isinstance(d, dict) else []
next_disabled = d.get('disabled', True) if isinstance(d, dict) else True
for bvid, title in plist:
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.
})()"""
res = ab_eval(page_js)
d = res if isinstance(res, dict) else {}
plist = d.get('list', []) if isinstance(d, dict) else []
next_disabled = d.get('disabled', True) if isinstance(d, dict) else True
for bvid, title in plist:
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.
})()"""
res = ab_eval(page_js)
d = res if isinstance(res, dict) else {}
plist = d.get('list', []) if isinstance(d, dict) else []
next_disabled = d.get('disabled', True) if isinstance(d, dict) else True
for bvid, title in plist:
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# 重试: fresh session 首次访问 SPA 偶尔不渲染, 主动 close --all + 重来一次
if not paginated_bvids_titles:
info("首次 抓 0, 可能是 fresh session 未能渲染, 主动 close --all + 重试")
subprocess.run(['agent-browser', 'close', '--all'], capture_output=True, timeout=15)
time.sleep(2)
ab_open(space_url)
time.sleep(8)
The comment export collects and persists user-linked metadata including username, mid, comment text, likes, and location without any user-facing disclosure or minimization. In a scraping skill that reuses browser cookies, this increases privacy risk and can facilitate profiling or bulk collection of personal data beyond what a user may expect.
While the outbound request itself is expected, this specific call retrieves comment data and associated user metadata using included browser credentials. In context, the more significant issue is undisclosed collection of personal data, so this network transfer meaningfully contributes to privacy risk.
if args.comments > 0:
aid = detail['aid']
comments_js = f"""(async () => {{
const r = await fetch('https://api.bilibili.com/x/v2/reply/main?oid={aid}&type=1&mode=3&next=0&ps={args.comments}', {{credentials:'include'}});
const j = await r.json();
const replies = (j?.data?.replies || []).map(rp => ({{
rpid: rp.rpid, uname: rp.member?.uname, mid: rp.member?.mid,
The skill description promises scraping uniformly through agent-browser with its cookie/signing environment, but danmaku retrieval silently uses a separate bilibili_api client. That bypass changes the trust boundary, network behavior, and authentication/session assumptions, which can expose different credentials, evade expected controls, or produce unreviewed outbound traffic.
Danmaku export writes user-linked metadata such as uid_hash and sender_id to disk without explicit disclosure. Even if partially pseudonymized, these identifiers can support tracking, correlation, or deanonymization when combined with other data sources.
Detected: suspicious.dynamic_code_execution