Back to skill

Security audit

Douyin Video Download

Security checks for vulnerabilities and agentic risk

Overview

This looks like a real Douyin video downloader, but it has review-worthy safety issues around unrestricted URLs, unsafe file paths, disabled browser sandboxing, and privileged installation guidance.

Install only if you are comfortable reviewing or fixing the safety gaps first. Avoid running it with elevated privileges, do not use the sudo yt-dlp one-liner without verifying the binary, restrict inputs to real Douyin HTTPS URLs, use a dedicated output directory, and avoid custom filenames containing path separators or .. components.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
lib/parser.js:143
Finding
Unrestricted URL Navigation and Download Enables SSRF-Like Access<![CDATA[ ## Vulnerability Details **File Location**: `lib/parser.js:143-148`, `lib/parser.js:95`, `lib/downloader.js:106-124` **Vulnerability Type**: Missing URL and destination validation **Risk Level**: High ### Complete Code Snippet ```js // lib/parser.js:143-148 async function parseDouyinUrl(inputUrl) { try { let targetUrl = inputUrl; if (inputUrl.includes('v.douyin.com')) { targetUrl = await resolveShortUrl(inputUrl); } const result = await fetchVideoInfo(targetUrl); ``` ```js // lib/parser.js:95 await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }); ``` ```js // lib/downloader.js:106-124 async function downloadVideo(videoUrl, outputDir, videoId, options = {}) { ensureDir(outputDir); const filename = options.filename || `${videoId}.mp4`; const filePath = path.join(outputDir, filename); console.log(` 🚀 正在尝试无水印解析下载...`); // 核心去水印链接构造 (严格白名单过滤) let downloadUrl = videoUrl; if (videoId && !videoUrl.includes('video_id=')) { // 仅允许字母、数字和下划线的 video_id,防止非法构造 if (/^[a-z0-9A-Z_]+$/.test(videoId)) { downloadUrl = `https://aweme.snssdk.com/aweme/v1/play/?video_id=${videoId}&ratio=1080p&line=0`; } } // 确保是 play 而不是 playwm downloadUrl = downloadUrl.replace('playwm', 'play'); try { const result = await downloadWithCurl(downloadUrl, filePath); ``` ### Technical Analysis The CLI accepts an arbitrary string as the target URL. The parser only checks whether the input contains the substring `v.douyin.com`; it does not parse the URL or enforce an exact hostname allowlist. For all other inputs, the supplied URL is passed directly to Playwright's `page.goto()`. This permits requests to arbitrary public or private HTTP services, including loopback, private-network, and link-local addresses. If parsing does not recover a valid video identifier, the original target URL can also remain the downloader's `downloadUrl`. The downloader then invokes `curl` with `-L`, allowing redire ...[truncated 1589 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every input with the standard `URL` class before performing any request. 2. Permit only `https:` URLs. 3. Enforce exact or boundary-aware hostnames, such as: - `douyin.com` - `www.douyin.com` - `v.douyin.com` - Explicitly required Douyin media domains 4. Do not use substring checks such as `includes('v.douyin.com')`, because domains such as `v.douyin.com.attacker.example` would pass. 5. Resolve hostnames and reject loopback, private, link-local, multicast, and reserved IP ranges for both IPv4 and IPv6. 6. Revalidate every redirect destination rather than allowing unrestricted browser or `curl -L` redirects. 7. Never pass the original URL to the media downloader when parsing fails. Abort unless a validated media URL was produced. 8. Add tests for malformed URLs, user-info hostname tricks, alternate IP encodings, DNS rebinding, and redirects to private addresses. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
lib/downloader.js:106
Finding
Path Traversal Through the Custom Filename Can Overwrite Arbitrary Writable Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/download.js:67-68`, `scripts/download.js:109-114`, `lib/downloader.js:106-108` **Vulnerability Type**: Path traversal and unsafe file overwrite **Risk Level**: High ### Complete Code Snippet ```js // scripts/download.js:67-68 } else if (arg === '--filename' && i + 1 < args.length) { result.filename = args[++i]; ``` ```js // scripts/download.js:109-114 const downloadResult = await downloader.downloadVideo( parseResult.targetUrl, options.output, parseResult.videoId, { filename: options.filename, timeout: options.timeout } ); ``` ```js // lib/downloader.js:106-108 async function downloadVideo(videoUrl, outputDir, videoId, options = {}) { ensureDir(outputDir); const filename = options.filename || `${videoId}.mp4`; const filePath = path.join(outputDir, filename); ``` ### Technical Analysis The value supplied through `--filename` is used directly as the second component of `path.join()` without normalization checks or basename enforcement. A filename containing parent-directory components, such as `../../target`, can escape the intended output directory. The resulting path is passed to `curl` using `-o`, which overwrites an existing writable file. The implementation does not use exclusive file creation, detect symbolic links, require confirmation, or verify that the resolved destination remains under the intended output directory. The documentation describes the option as a filename without an extension, but the implementation neither restricts it to a filename nor automatically adds the expected extension. ### Attack Path 1. A user or automation invokes the Skill with a malicious filename, for example: ```bash node scripts/download.js "https://www.douyin.com/video/123456" \ --output ./videos \ --filename ../../target-file ``` 2. The CLI stores the supplied value without validation. 3. `path.join(outputDir, filename)` resolves the `..` components and ...[truncated 875 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict custom filenames to a conservative basename pattern, for example letters, digits, spaces, underscores, and hyphens. 2. Reject: - `/` and `\` - `.` and `..` path components - Control characters - Platform-specific reserved filenames 3. Resolve and validate the final path: ```js const base = path.resolve(outputDir); const safeName = path.basename(filename); const destination = path.resolve(base, safeName); if (path.dirname(destination) !== base) { throw new Error('Invalid output filename'); } ``` 4. Append or enforce the expected `.mp4` extension after validation. 5. Use exclusive file creation where possible and refuse to overwrite existing files unless the user explicitly opts in. 6. Check for symbolic links before writing and avoid following links in attacker-writable output directories. 7. Run the Skill as an unprivileged account and never recommend elevated execution for normal downloads. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
lib/parser.js:16
Finding
Chromium Is Launched With Its Security Sandbox Disabled<![CDATA[ ## Vulnerability Details **File Location**: `lib/parser.js:16-20`, `lib/parser.js:66-70` **Vulnerability Type**: Browser sandbox bypass and excessive process privileges **Risk Level**: High ### Complete Code Snippet ```js // lib/parser.js:16-20 try { browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); ``` ```js // lib/parser.js:66-70 try { browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); ``` ### Technical Analysis Both Chromium launch paths explicitly pass `--no-sandbox` and `--disable-setuid-sandbox`. These flags disable Chromium's principal operating-system process isolation mechanisms. The browser loads remote, active web content. Because URL destinations are not adequately restricted, this includes potentially attacker-controlled pages rather than only trusted Douyin pages. If the browser renderer or another browser component contains an exploitable vulnerability, disabling the sandbox removes an important containment boundary. The flags are not necessary for the declared video-download functionality on a properly configured host. They are commonly used as a workaround for containers or privileged execution environments, but that workaround transfers browser compromise directly to the privileges of the Node.js process. ### Attack Path 1. An attacker causes the Skill to parse an attacker-controlled URL or compromises content loaded by a legitimate target page. 2. The Skill launches Chromium with the sandbox disabled. 3. Chromium processes attacker-controlled HTML, JavaScript, media, and network responses. 4. The attacker exploits a browser-engine vulnerability. 5. Because the sandbox is disabled, exploit code runs with the effective privileges of the Skill process without needing a separate sandbox escape. 6. The resulting process may access files, network resources, and subprocess capabilities available to t ...[truncated 609 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--no-sandbox` and `--disable-setuid-sandbox` from both launch configurations. 2. Run Chromium as a dedicated, unprivileged operating-system user. 3. Configure containers to support Chromium's sandbox rather than disabling it. 4. Do not run the Skill or Chromium as root. 5. Restrict browser navigation to an explicit HTTPS hostname allowlist. 6. Keep Playwright and Chromium pinned to a reviewed, supported version and apply security updates promptly. 7. Apply additional isolation through a container or operating-system sandbox with: - A read-only root filesystem - A dedicated writable download directory - No host credential mounts - Restricted outbound network access - Dropped Linux capabilities - Resource and process limits ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:34
Finding
External Executable Installation Uses a Mutable Release Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:34-38` **Vulnerability Type**: Unverified third-party executable installation **Risk Level**: Medium ### Complete Code Snippet ```markdown ### 2. 安装外部工具 (可选但推荐) - **yt-dlp**: 提供最佳下载体验和更高的稳定性。 - **Linux/macOS**: `sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp && sudo chmod a+rx /usr/local/bin/yt-dlp` - **Windows**: 从 [yt-dlp releases](https://github.com/yt-dlp/yt-dlp/releases) 下载 `.exe` 并添加到 PATH。 ``` The downloaded program is later executed by the downloader: ```js // lib/downloader.js:136-140 try { console.log(` 🔄 尝试 yt-dlp 备选通道...`); const ytArgs = ['-o', filePath, '--user-agent', 'Mozilla/5.0', downloadUrl]; await runCommand('yt-dlp', ytArgs); ``` ### Technical Analysis The documented Linux/macOS installation command downloads the mutable `latest` release and writes it directly into `/usr/local/bin` using elevated privileges. It does not pin a reviewed version or verify a checksum or cryptographic signature before making the file executable. The Windows instructions similarly tell users to download and execute a release artifact without describing integrity verification. The referenced host is the official `github.com/yt-dlp/yt-dlp` release location. Therefore, the static pre-scan characterization of the source as a personal hosting or pastebin site is not supported by the reviewed content. Nevertheless, relying solely on HTTPS and a mutable release URL is weaker than explicit artifact verification. Because the program is placed on `PATH`, the Skill later executes it automatically as a fallback. This converts an installation-time supply-chain compromise into local code execution with the Skill user's privileges. ### Attack Path 1. A user follows the documented installation command. 2. The command downloads whichever artifact is currently served by the mutable `latest` URL. 3. No expected version, digest, or publisher ...[truncated 951 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer a trusted operating-system package manager where an appropriately maintained package is available. 2. If downloading a standalone binary: - Pin an explicit yt-dlp version. - Download from the official release page. - Obtain the publisher-provided checksum or signature through a trusted channel. - Verify the artifact before making it executable or placing it on `PATH`. 3. Download to a temporary, non-privileged location first; only move the verified artifact into its final destination. 4. Avoid piping or downloading mutable remote content directly into privileged PATH locations. 5. Where system-wide installation is unnecessary, install the verified executable in a user-owned directory with narrowly scoped permissions. 6. Document the same verification procedure for Windows executables. 7. Consider making `yt-dlp` a truly optional fallback in metadata instead of declaring it as an unconditional required binary. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
该代码确实与“抖音视频下载/无水印下载”这一核心方向一致:它会尝试将 playwm 替换为 play,并基于 videoId 构造 aweme 播放链接,再用 curl 下载,失败后回退到 yt-dlp。但声明中提到的多项关键能力在此代码中没有体现:1) 没有批量处理逻辑,只看到 downloadVideo 的单视频入口;2) 没有任何去重判断或已下载记录管理;3) 宣称智能选择 yt-dlp/Playwright,但代码仅在 curl 与 yt-dlp 之间切换,完全没有 Playwright;4) 因此描述比实际代码覆盖的能力更广,存在明显描述-行为不一致。

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
The code clearly matches the general purpose of a Douyin video downloader and does support single and batch modes. However, the description makes specific capability claims—automatic deduplication, watermark-free downloading, and intelligent selection of yt-dlp/Playwright—that are not substantiated by this code chunk. The batch mode simply reads URLs from a file and downloads them one by one. There is also a --concurrent parameter, but the loop awaits each download sequentially, so concurrency is not implemented here. This is therefore a description/behavior mismatch, though the primary purpose remains broadly aligned.

Chaining Abuse

High
Category
Tool Misuse
Content
### 2. 安装外部工具 (可选但推荐)

- **yt-dlp**: 提供最佳下载体验和更高的稳定性。
  - **Linux/macOS**: `sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp && sudo chmod a+rx /usr/local/bin/yt-dlp`
  - **Windows**: 从 [yt-dlp releases](https://github.com/yt-dlp/yt-dlp/releases) 下载 `.exe` 并添加到 PATH。

## 使用
Confidence
87% confidence
Finding
The chained command combines download and privileged permission changes in one line, which reduces opportunities for inspection and safe validation between steps. While `&&` itself is not malicious, in this context it streamlines execution of an unverified remote binary into a privileged path, increasing operational risk and making accidental unsafe execution more likely.

Ae1

High
Category
analysis-evasion
Content
node scripts/download.js "https://v.douyin.com/xxxxx"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/download.js "https://v.douyin.com/xxxxx"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/download.js "https://v.douyin.com/xxxxx"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/download.js "https://v.douyin.com/xxxxx"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/download.js "https://v.douyin.com/xxxxx"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/download.js "https://v.douyin.com/xxxxx"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Natural-Language Policy Violations

High
Confidence
95% confidence
Finding
The code and comments explicitly describe using Playwright to bypass anti-scraping protections and construct direct no-watermark download URLs. That behavior is dangerous in a skill context because it normalizes deliberate evasion of platform controls, increases legal/compliance risk, and can lead to account, IP, or service abuse even if it is not a classic memory-corruption bug.

Known Vulnerable Dependency: axios==1.13.6 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
95% confidence
Finding
The lockfile pins axios 1.13.6, which the static analysis indicates is affected by multiple advisories including SSRF/proxy handling issues and prototype-pollution-related request manipulation. In a video download tool that fetches attacker-controlled URLs or follows redirected content, a vulnerable HTTP client materially increases risk of server-side request forgery, credential leakage, or request/response tampering.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
83% confidence
Finding
form-data 4.0.5 is reported vulnerable to CRLF injection through unescaped multipart field names and filenames. If the skill ever builds multipart requests using attacker-influenced metadata, this can enable malformed requests, header injection, or request smuggling behavior against downstream services.

Known Vulnerable Dependency: undici==7.24.0 — 12 advisory(ies): CVE-2026-6733 (undici vulnerable to HTTP response queue poisoning via keep-alive socket reuse); CVE-2026-13697 (undici vulnerable to cross-user information disclosure and parse-time crash via ); CVE-2026-16728 (undici vulnerable to downstream response desynchronization via retry interceptor) +9 more

High
Category
Supply Chain
Confidence
88% confidence
Finding
undici 7.24.0 is flagged for multiple HTTP parsing and response desynchronization issues. Because this package is brought in via cheerio and may be used for remote fetches in a downloader that processes untrusted network responses, these flaws could lead to request confusion, cache/connection poisoning, or cross-request data exposure in certain deployment patterns.

Known Vulnerable Dependency: axios==1.13.6 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
94% confidence
Finding
The dependency set permits or resolves to an axios version with known high-severity advisories, including SSRF-related and prototype-pollution/MITM-style issues per the scanner output. In a video download tool that fetches remote URLs and web content, a vulnerable HTTP client is more dangerous because attacker-controlled endpoints, redirects, proxy settings, or crafted responses may be processed during normal operation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill documentation indicates use of shell and environment-dependent capabilities but does not declare any explicit tool scope such as permissions or allowed-tools. This can cause the agent runtime to grant broader-than-expected execution capability, reducing transparency and increasing the chance of unsafe command execution when the skill is invoked.

Rp1

Medium
Category
MCP Rug Pull
Confidence
84% confidence
Finding
Using `npx playwright install chromium` without a pinned package version allows the resolved Playwright package to vary over time, creating a supply-chain risk. A future compromised or incompatible release could be fetched and executed during installation, especially because `npx` may download code dynamically.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### 2. 安装外部工具 (可选但推荐)

- **yt-dlp**: 提供最佳下载体验和更高的稳定性。
  - **Linux/macOS**: `sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp && sudo chmod a+rx /usr/local/bin/yt-dlp`
  - **Windows**: 从 [yt-dlp releases](https://github.com/yt-dlp/yt-dlp/releases) 下载 `.exe` 并添加到 PATH。

## 使用
Confidence
90% confidence
Finding
The instructions recommend downloading an executable from the internet with `curl` and placing it into `/usr/local/bin` using `sudo`. This encourages privileged installation of remote code without signature or checksum verification, so a compromised release, MITM in a misconfigured environment, or user copy-paste error could lead to system-wide code execution as root.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This file contains natural-language strings such as comments and console messages exclusively in Chinese, which imposes a specific language on operators or developers. The policy allows fixed locale behavior only when user choice or a justified locale constraint is provided, which is not present here.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This code navigates a browser to a user-supplied URL, which transmits the provided link and browser metadata to external Douyin infrastructure. Although there are console logs about parsing progress, there is no warning or comment disclosing that the skill will contact third-party services and fetch remote page content on the user's behalf.

Missing User Warnings

Medium
Confidence
80% confidence
Finding
The response handler inspects network responses from the loaded page and extracts video identifiers from API payloads. This is a privacy-relevant network-data handling behavior, but the surrounding comments only describe technical optimization and do not warn users that third-party response data will be programmatically captured and parsed.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This code file contains natural-language strings for the banner, help text, and usage instructions almost entirely in Chinese. Under the policy rule, forcing a specific language without user opt-in is a locale/language policy violation when no alternative language selection or justification is provided.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The file’s user-facing description and instructions are consistently presented in Chinese, and there is no indication that users may choose another language or that the skill is intentionally limited to Chinese-speaking users. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Known Vulnerable Dependency: follow-redirects==1.15.11 — 1 advisory(ies): CVE-2026-40895 (follow-redirects leaks Custom Authentication Headers to Cross-Domain Redirect Ta)

Low
Category
Supply Chain
Confidence
86% confidence
Finding
follow-redirects 1.15.11 is flagged for leaking custom authentication headers across cross-domain redirects. While this downloader may not primarily handle sensitive bearer tokens, any authenticated outbound requests, cookies, or custom headers used for scraping/downloading could be exposed to attacker-controlled redirect targets.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The natural-language description forces a specific language in user-facing package metadata, which can violate language/locale policy when no opt-in or scope limitation is stated. The file does not indicate that the package is intended only for a Chinese-speaking or region-specific audience.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
lib/downloader.js:34