Back to skill

Security audit

douyin-video-read

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate Douyin video-reading purpose, but it also opens and captures content from arbitrary video pages with weak URL validation, which needs review before installation.

Install only if you are comfortable with an agent launching a browser, contacting supplied URLs, and saving video-derived images and transcripts locally. Prefer using it only with Douyin links you trust, run it in a network-restricted environment, choose an output directory deliberately, and delete generated frames/transcripts when no longer needed.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/read_douyin.py:32
Finding
Insufficient URL Validation Permits Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/read_douyin.py:32-49` - `scripts/capture_frames.py:42-63` **Vulnerability Type**: Server-Side Request Forgery (SSRF) through unvalidated user-supplied URLs **Risk Level**: Medium ### Vulnerable Code `scripts/read_douyin.py:32-49`: ```python def extract_video_id(text: str) -> str: """从分享文本中解析视频 ID。""" urls = re.findall(r"https?://[^\s\u201c\u201d]+", text) if not urls: raise ValueError("未在输入中找到链接") url = urls[0].rstrip('/') m = re.search(r'/video/(\d+)', url) if m: return m.group(1) m = re.search(r'(\d{15,})', url) if m: return m.group(1) # 短链:跟随 302 取真实地址 resp = requests.get(url, headers={"User-Agent": UA_MOBILE}, allow_redirects=True, timeout=20) m = re.search(r'/video/(\d+)', resp.url) if not m: m = re.search(r'(\d{15,})', resp.url) if not m: raise ValueError(f"无法从 {resp.url} 解析视频 ID") return m.group(1) ``` `scripts/capture_frames.py:42-63`: ```python def resolve_target(text: str) -> dict: """把用户输入解析成 (要打开的页面地址, 平台, 视频 ID)。""" url = first_url(text) if "douyin.com" in url: m = re.search(r"/video/(\d+)", url) or re.search(r"(\d{15,})", url) if m: vid = m.group(1) else: # 短链,跟随 302 try: resp = requests.get(url, headers={"User-Agent": UA_MOBILE}, allow_redirects=True, timeout=20) m = re.search(r"/video/(\d+)", resp.url) or re.search(r"(\d{15,})", resp.url) vid = m.group(1) if m else None except Exception: vid = None if not vid: raise ValueError(f"无法从 {url} 解析抖音视频 ID") return {"page_url": f"https://www.douyin.com/video/{vid}", "platform": "douyin", "video_id": vid, "warmup": "https://www.douyin.com/"} return {"page_url": url, "platform": "generic", "video_id": None, "warmup": None} `` ...[truncated 2841 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every URL with `urllib.parse.urlsplit` rather than using regular expressions or substring checks. 2. Require HTTPS for Douyin short-link resolution. 3. Enforce an exact, case-normalized hostname allowlist, for example: - `douyin.com` - `www.douyin.com` - `v.douyin.com` 4. Reject userinfo, malformed ports, IP-literal hosts, and hostnames with unexpected suffixes. 5. Resolve the hostname before connecting and reject every address that is loopback, private, link-local, multicast, reserved, or unspecified using Python's `ipaddress` module. 6. Disable automatic redirects and validate each `Location` destination before following it. Apply the same scheme, hostname, and resolved-address validation at every redirect hop. 7. If generic URL capture must remain available, place it behind an explicit opt-in flag and clearly warn that it allows outbound navigation. 8. Run browser and HTTP operations in a network-restricted sandbox that cannot reach cloud metadata services or internal administrative networks. 9. Add regression tests covering deceptive hostnames, embedded credentials, alternate IP representations, IPv6 addresses, DNS rebinding, and external-to-private redirects. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:4
Finding
Unpinned Runtime Dependencies and Browser Artifacts Create Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Locations**: - `requirements.txt:4-8` - `SKILL.md:38-39` - `README.md:40-41` **Vulnerability Type**: Unpinned third-party dependencies and unverified browser downloads **Risk Level**: Low ### Vulnerable Code `requirements.txt:4-8`: ```text # L1 + L2 必需 playwright>=1.40 requests>=2.28 # L2 字幕裁剪必需 Pillow>=10.0 ``` `SKILL.md:38-39`: ```bash pip install playwright requests Pillow python -m playwright install chromium # 可跳过:会优先复用系统已装的 Edge / Chrome ``` `README.md:40-41`: ```bash pip install playwright requests Pillow python -m playwright install chromium # 或跳过:直接用系统已装的 Edge / Chrome ``` ### Technical Analysis The dependency declarations specify only minimum versions. An installation performed after this audit can therefore select future releases that were not reviewed with the project. The documentation further recommends installing packages without version constraints and downloading a Chromium artifact through Playwright. This does not demonstrate that any currently named package is malicious. The package names are consistent with the application's documented functionality. The issue is that installation is not reproducible and does not cryptographically bind users to an audited dependency set. Python package installation may execute package build or installation logic, while the Playwright command downloads a substantial executable browser artifact determined by the installed Playwright version. If an upstream package, package index, account, or distribution channel is compromised, users following these instructions could install code different from the code assessed during this audit. ### Attack Path 1. A user follows the installation instructions in `README.md` or `SKILL.md`. 2. `pip` resolves the latest versions satisfying the open-ended constraints, or entirely unconstrained versions from the documentation command. 3. The resolved packages may differ from those tested by the project author or reviewed ...[truncated 992 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace open-ended minimum constraints with reviewed, exact versions. 2. Generate and commit a reproducible lockfile for supported platforms and Python versions. 3. Include hashes for all distributions and install with: ```bash pip install --require-hashes -r requirements.lock ``` 4. Update `README.md` and `SKILL.md` so their installation commands use the audited lockfile instead of unconstrained package names. 5. Document the expected package index and avoid untrusted additional indexes. 6. Pin the Playwright version so its expected Chromium revision is deterministic. 7. Document the expected browser revision and verify downloaded artifacts through the official Playwright distribution mechanism. 8. Perform dependency updates through a controlled review process that includes vulnerability scanning, changelog review, and regression testing. 9. Consider installing dependencies in an isolated virtual environment or container with restricted privileges and network access. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
声明描述的是一个面向抖音视频内容理解与文稿提取的技能,核心能力应包括读取元信息、获取官方 AI 章节要点,以及基于逐帧截图+OCR产出完整文字稿。实际代码只是一个浏览器自动化抓帧工具:解析抖音或普通 URL、打开页面、定位 video 元素、seek 到各时间点,用 canvas 截取视频帧并保存,同时输出少量技术元数据。代码中没有任何 OCR、ASR、字幕提取、AI 章节读取、文本总结或文案抽取逻辑,因此与声明的主要用途存在实质性偏差。虽然“逐帧截图”是声明中管线的一部分,但这里只实现了前半段抓帧,缺失了将帧转换为文字与内容分析的关键功能。

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
代码的核心行为与声明部分重合在“读取抖音视频元信息”和“提取官方 AI 章节要点”上,这部分描述基本准确。但声明强调还能通过逐帧截图和字幕 OCR 得到完整口播讲稿,适用于“提取视频文案”“抖音视频转文字”等场景;而提供的代码并没有任何视频帧提取、字幕区域检测、OCR 调用、音频转写或讲稿拼接逻辑。它只是打开抖音网页,关闭弹窗,读取 document.body.innerText,再基于文本规则解析标题、作者、发布时间、互动数据和章节要点。虽然支持可选页面截图保存,但那只是整页截图,不是逐帧截图,也未被后续 OCR 使用。因此,声明对技能能力有实质性夸大,属于描述与实际行为不一致。

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README instructs users to extract, OCR, and store video frames, subtitles, transcripts, comments, and related metadata, but it provides no privacy, retention, or sensitivity warning. This omission matters because the generated artifacts may contain personal data, copyrighted material, or sensitive on-screen text that could be retained or redistributed without informed handling.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The README explicitly states that the frame-capture and OCR tooling works for any page containing an HTML5 video element, which expands the skill beyond its declared Douyin-reading purpose. That scope expansion increases the chance the skill is used for arbitrary content extraction or surveillance-like collection against unrelated sites without the tighter constraints users would expect from a Douyin-specific skill.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
Documenting arbitrary-URL video frame extraction creates a generic content-harvesting capability not justified by the stated business purpose of reading Douyin videos. In practice, such a feature can be repurposed to capture frames and subtitle text from any accessible video page, broadening abuse potential and weakening least-privilege expectations.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill documents shell execution, network access, and file read/write behavior but does not declare any tool scope or allowed-tools boundary. In an agent ecosystem, that increases the chance the skill is invoked with broader privileges than users expect, enabling unintended network requests, local file creation, and command execution.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger list includes broad everyday phrases like '看看这个视频' and '视频内容', which can cause accidental activation outside the intended Douyin-only context. In an agent setting, overbroad triggering can lead to unintended browsing, external requests, and local artifact generation without sufficiently specific user intent.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# B. 跨平台
#   macOS:  brew install tesseract tesseract-lang
#   Ubuntu: sudo apt install tesseract-ocr tesseract-ocr-chi-sim
pip install pytesseract   # 可选,装了会自动优先走 Python 接口

# C. 都不装:用 --backend none,把裁好的字幕图交给具备视觉能力的 agent 自己读
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# B. 跨平台
#   macOS:  brew install tesseract tesseract-lang
#   Ubuntu: sudo apt install tesseract-ocr tesseract-ocr-chi-sim
pip install pytesseract   # 可选,装了会自动优先走 Python 接口

# C. 都不装:用 --backend none,把裁好的字幕图交给具备视觉能力的 agent 自己读
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill creates screenshots, OCR outputs, and transcripts on disk but does not prominently warn users before doing so. Because these artifacts may contain copyrighted, sensitive, or personal data from video content, silent persistence increases privacy and data-handling risk.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The script is advertised as a Douyin-video reader, but the implementation explicitly supports arbitrary webpages containing an HTML5 <video> element. That scope expansion can be abused to capture frames from non-Douyin content, including internal or sensitive web applications reachable from the runtime environment, turning the skill into a generic browser-based content extraction tool.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The WinRT OCR path first hard-codes `Language("zh-Hans-CN")`, which imposes a specific language/locale choice without user opt-in. This matches the policy category for language or locale constraints, even though a fallback to user profile languages exists only if the forced locale engine cannot be created.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
for p in paths:
        try:
            proc = subprocess.run(
                ["tesseract", p, "stdout", "-l", lang],
                capture_output=True, timeout=120)
            text = proc.stdout.decode("utf-8", "replace") if proc.stdout else ""
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The file’s natural-language instructions and defaults are explicitly Chinese-centric, including Chinese-only subtitle assumptions and a default OCR language of `chi_sim+eng`. This imposes a locale/language constraint without offering a user-facing choice in the skill description or documenting why the restriction is required for a region-specific use case.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest says the skill can obtain a complete spoken script through per-frame screenshots plus subtitle OCR. In this file, the runtime behavior is limited to opening the Douyin page, optionally taking a single page screenshot, and returning document.body.innerText for downstream parsing; there is no frame extraction loop, subtitle detection, or OCR processing.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
SQP-3 适用于所有文件类型。文档整体以“中文好”“系统中文 OCR 语言包”“抖音视频转文字”等中文处理为默认前提,但未明确说明该技能仅面向中文视频场景,或在非中文内容时提供用户选择,存在语言/locale 约束未声明的问题。

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The natural-language comments throughout the file are written only in Chinese, which imposes a specific language choice on readers without offering an alternative or documenting a justified locale-specific scope. Under the policy, forcing a specific language without user opt-in can be a locale/language policy violation.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# 依赖(按需安装,不是全都要)

# L1 + L2 必需
playwright>=1.40
requests>=2.28

# L2 字幕裁剪必需
Confidence
90% confidence
Finding
Using an unpinned dependency range for Playwright allows different versions to be installed over time, which can introduce breaking changes or newly disclosed vulnerabilities into the environment without review. In a skill that automates browser interaction against external Douyin URLs, dependency drift increases supply-chain and runtime risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# L1 + L2 必需
playwright>=1.40
requests>=2.28

# L2 字幕裁剪必需
Pillow>=10.0
Confidence
96% confidence
Finding
Using requests>=2.28 permits installation of multiple future or currently vulnerable versions, making security posture unverifiable and potentially exposing the skill to known HTTP client issues. Because this skill fetches and processes external video links, a vulnerable HTTP library could increase exposure to credential leakage, request smuggling, or TLS-related flaws depending on the deployed version.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
95% confidence
Finding
The manifest references requests without pinning a version, and requests has multiple known advisories across releases, so the deployed package may be vulnerable without any way to verify from this file alone. In a skill that makes network requests to user-supplied Douyin links, this uncertainty materially increases supply-chain and transport-layer risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.28

# L2 字幕裁剪必需
Pillow>=10.0

# OCR 后端 A:Windows 内置 OCR(Windows 专用,推荐)
# winrt-runtime
Confidence
96% confidence
Finding
Pillow>=10.0 leaves the exact installed image-processing version undefined, which is risky because image libraries frequently receive fixes for memory corruption and denial-of-service issues. This skill handles screenshots and OCR preprocessing of video frames, so malformed or attacker-controlled image content could increase the impact of an outdated or vulnerable Pillow release.

Unverifiable Dependency: Pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
95% confidence
Finding
The manifest references Pillow without pinning a version even though Pillow has a history of security advisories, so the installed package may include exploitable flaws. Given that this skill processes image data derived from external video content, an unsafe Pillow version could enable denial of service or worse through crafted image inputs.

Missing User Warnings

Low
Confidence
87% confidence
Finding
This code sends a request to a user-provided Douyin short link to resolve redirects, which is a network operation involving user-supplied data. Although the module docstring explains the overall purpose, it does not explicitly warn that the script will make outbound HTTP requests before browser automation begins.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The code creates output directories and later writes JPEG frames and a JSON metadata file. While the --out argument names an output directory, there is no explicit warning in the description or startup output that many files will be created and persisted there.

Intent-Code Divergence

Low
Confidence
71% confidence
Finding
The top-level documentation presents interaction data retrieval as a direct capability. However, the implementation later guesses the four stats by finding consecutive numeric lines after a detected title, which may not correspond to authoritative interaction fields and is materially different from the documented intent.

Static analysis

No suspicious patterns detected.