Back to skill

Security audit

Douyin Video Skills

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its Douyin video and transcript purpose, but it needs Review because it handles login profiles, API keys, external audio upload, and broad unvalidated network downloads.

Install only if you are comfortable using a dedicated Douyin browser profile, protecting the profile and API key like credentials, sending extracted audio to SiliconFlow for transcription, and storing transcript/media outputs locally. Prefer a sandboxed environment, pinned dependencies, a throwaway or low-risk account, restricted output directories, and avoid passing untrusted links until URL validation and download limits are added.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/douyin_downloader.py:72
Finding

Server-Side Request Forgery Through Unrestricted Share URL Fetching

Content
View full analysis

Vulnerability Details

File Location: scripts/douyin_downloader.py, lines 72–78
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: High

Vulnerable Code:

python
urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', share_text)
if not urls:
    raise ValueError("未找到有效的分享链接")

share_url = urls[0]
share_response = requests.get(share_url, headers=HEADERS)
share_response.raise_for_status()
video_id = share_response.url.split("?")[0].strip("/").split("/")[-1]

Technical Analysis

The downloader extracts the first HTTP or HTTPS URL from user-controlled input and sends a request to it without validating the destination. There is no hostname allowlist, port restriction, resolved-address check, or redirect validation. Because requests.get() follows redirects by default, an initially public URL can also redirect the process to a loopback, private, link-local, or cloud metadata address.

The Skill only needs to resolve legitimate Douyin links. Allowing requests to arbitrary hosts exceeds the minimum network privileges necessary for that functionality.

Attack Path

  1. An attacker supplies a crafted --link value containing a URL such as http://127.0.0.1:<port>/, a private-network endpoint, or an attacker-controlled redirector.
  2. parse_share_url() accepts the URL because it only checks its textual HTTP(S) format.
  3. The process sends the request using its own network access.
  4. If redirects are involved, requests follows them without validating each destination.
  5. The attacker can trigger requests to services reachable from the execution environment and observe errors, timing, or externally visible side effects.

Impact Assessment

Successful exploitation can allow internal network probing, interaction with localhost or intranet services, and attempts to reach cloud instance metadata endpoints. Although th ...[truncated 280 chars]

Remediation
View remediation

Remediation Suggestions

  • Accept only HTTPS URLs belonging to an explicit allowlist of required Douyin domains.
  • Reject URLs containing embedded credentials, fragments, nonstandard ports, or malformed hostnames.
  • Resolve the hostname before connecting and reject loopback, private, link-local, multicast, reserved, and unspecified IP ranges for both IPv4 and IPv6.
  • Disable automatic redirects or validate the scheme, hostname, port, and resolved address of every redirect target.
  • Apply connection and read timeouts.
  • Prefer extracting a numeric video ID locally when the input already uses the documented stable share-link format.
  • Add automated tests covering localhost, private addresses, IPv6 loopback, DNS rebinding scenarios, and public-to-private redirects.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/douyin_downloader.py:119
Finding

Unbounded Remote Video Download Can Exhaust Resources

Content
View full analysis

Vulnerability Details

File Location: scripts/douyin_downloader.py, lines 119–132
Vulnerability Type: Unrestricted Download Size and Missing Network Timeouts
Risk Level: Medium

Vulnerable Code:

python
response = requests.get(video_info['url'], headers=HEADERS, stream=True)
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))

downloaded = 0
with open(filepath, 'wb') as f:
    for chunk in response.iter_content(chunk_size=8192):
        if chunk:
            f.write(chunk)
            downloaded += len(chunk)
            if show_progress and total_size > 0:
                progress = downloaded / total_size * 100
                print(f"\r下载进度: {progress:.1f}%", end="", flush=True)

Technical Analysis

The media download has no connection timeout, read timeout, maximum response size, transfer-duration limit, or content-type validation. The Content-Length value is used only to display progress and is not enforced. A server may omit or falsify that header and stream an arbitrarily large or endless response.

The resulting file is subsequently passed to FFmpeg, increasing resource consumption and exposing a media parser to content that has not been verified as an expected media type.

Attack Path

  1. A malicious or compromised remote endpoint supplies a media URL that returns an oversized response, streams indefinitely, or sends data very slowly.
  2. The downloader opens a temporary file and writes every received chunk without enforcing a byte limit.
  3. The process remains blocked or continues writing until the server closes the connection, local storage is exhausted, or an external operator terminates it.
  4. If the transfer completes, the unexpected file may then be processed by FFmpeg.

Impact Assessment

Exploitation can cause disk exhaustion, prolonged worker occupation, excessive bandwidth use, and denial of service for the use ...[truncated 283 chars]

Remediation
View remediation

Remediation Suggestions

  • Set explicit connection and read timeouts, for example through a bounded timeout=(connect_timeout, read_timeout) value.
  • Define a maximum accepted video size and stop streaming as soon as the accumulated byte count exceeds it.
  • Reject a declared Content-Length above the configured limit before downloading.
  • Do not rely solely on Content-Length, because it may be absent or deceptive.
  • Validate the response content type against a narrow set of expected video formats.
  • Enforce an overall transfer deadline and a minimum transfer rate where operationally appropriate.
  • Delete partial files reliably on all failures.
  • Apply storage quotas and run media processing with constrained CPU, memory, execution time, and filesystem access.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/douyin_downloader.py:325
Finding

API Key May Be Exposed Through Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: scripts/douyin_downloader.py, lines 325–335
Vulnerability Type: Sensitive Information Exposure Through Process Arguments
Risk Level: Low

Vulnerable Code:

python
parser.add_argument("--api-key", "-k", help="硅基流动 API 密钥 (也可通过 API_KEY 环境变量设置)")
parser.add_argument("--save-video", "-v", action="store_true", help="提取文案时同时保存视频")
parser.add_argument("--quiet", "-q", action="store_true", help="安静模式")
args = parser.parse_args()

try:
    if args.action == "info":
        info = get_video_info(args.link)
        print(json.dumps(info, ensure_ascii=False, indent=2))
    elif args.action == "download":
        video_path = download_video(args.link, args.output)
        print(str(video_path))
    elif args.action == "extract":
        result = extract_text(args.link, args.api_key, output_dir=args.output, save_video=args.save_video, show_progress=not args.quiet)

Technical Analysis

The script permits a SiliconFlow API key to be supplied directly as a command-line argument. Command lines may be retained in shell history, captured by job runners and monitoring platforms, included in diagnostic logs, or visible to other local processes subject to operating-system access controls.

Environment-variable retrieval is already supported, so exposing a command-line secret option is not required for the declared transcription functionality.

Attack Path

  1. A user invokes the extractor with --api-key or -k followed by the secret.
  2. The shell may save the complete command in its history.
  3. While the process is running, local process-inspection facilities or monitoring agents may capture its argument vector.
  4. Logs or history files containing the key may persist after the transcription completes.
  5. A local attacker or unauthorized log reader obtains and reuses the credential.

Impact Assessment

Exposure can allow unauthorized SiliconFlow API ...[truncated 260 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove the --api-key and -k command-line options.
  • Retrieve the credential from a narrowly named environment variable, operating-system keyring, managed secret store, or permission-restricted configuration file.
  • If interactive entry is needed, use a hidden prompt rather than normal terminal input.
  • Ensure errors, debug output, subprocess diagnostics, and generated metadata never contain the key.
  • Document restrictive file permissions if ~/.openclaw/.env is used.
  • Rotate any key that may previously have appeared in shell history, process telemetry, or CI logs.

T08 · Insecure Dependencies

Note
Location
SKILL.md:30
Finding

Mutable and Unpinned Third-Party Dependency Installation

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 30–48
Vulnerability Type: Unpinned Third-Party Dependencies
Risk Level: Low

Vulnerable Code:

bash
pip install requests ffmpeg-python
bash
npm install -g @playwright/cli@latest
bash
playwright-cli install --skills

Technical Analysis

The installation instructions retrieve Python and npm packages without fixed versions or integrity hashes. The explicit @latest selector causes the installed Playwright CLI implementation to change over time without corresponding changes to the reviewed Skill package. The subsequent install --skills operation may also install additional content whose exact version and integrity are not established by this repository.

No typosquatted package or known malicious source was identified. The risk arises from mutable, non-reproducible dependency resolution and global installation rather than evidence that the named dependencies are currently malicious.

Attack Path

  1. A user follows the documented installation commands.
  2. Package registries resolve the commands to whatever versions are current at installation time.
  3. A compromised upstream account, malicious future release, registry compromise, or unsafe transitive dependency is downloaded.
  4. Installation scripts or package code execute with the user's privileges.
  5. Because the npm package is installed globally, its effects may extend beyond this Skill's isolated environment.

Impact Assessment

A compromised dependency could execute arbitrary code with the privileges of the installing or invoking user, access files available to that user, inspect browser state or environment variables, and alter globally installed tooling. The actual impact depends on user privileges and environment isolation. This is a supply-chain hardening issue; the audit found no evidence of present malicious dependency content.

Remediation
View remediation

Remediation Suggestions

  • Pin all direct dependencies to reviewed versions.
  • Use a Python requirements or lock file with cryptographic hashes where practical.
  • Replace @latest with an exact reviewed npm package version.
  • Avoid global npm installation; use a project-local dependency and invoke it through the local package runner.
  • Lock and review transitive dependencies.
  • Document the expected package registries and prevent fallback to untrusted registries.
  • Record and verify checksums or signatures for externally installed Skill content.
  • Re-audit dependency updates before changing pinned versions.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (27)

Tainted flow: 'video_info' from os.getenv (line 269, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The downloader fetches video_info['url'] obtained from parsed remote HTML/JSON and then streams the response to disk without verifying the destination domain, content type, or size. If the upstream page is manipulated or parsing is abused, the runtime can be induced to contact unexpected endpoints and download arbitrary large content, leading to secondary SSRF and disk/bandwidth exhaustion.

Content

Scanner excerpt · scripts/douyin_downloader.py (reported line 118)May include surrounding context.

python
if show_progress:
            print(f"正在下载视频: {video_info['title']}")

        response = requests.get(video_info['url'], headers=HEADERS, stream=True)
        response.raise_for_status()
        total_size = int(response.headers.get('content-length', 0))

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This mismatch specifically affects data handling expectations: the skill is presented as an end-to-end acquisition and verification workflow, but appears to perform a narrower local text-processing role in some paths. Such ambiguity can cause users to supply browser profiles, API keys, or content under false assumptions about what processing actually occurs and where data flows.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This mismatch specifically affects data handling expectations: the skill is presented as an end-to-end acquisition and verification workflow, but appears to perform a narrower local text-processing role in some paths. Such ambiguity can cause users to supply browser profiles, API keys, or content under false assumptions about what processing actually occurs and where data flows.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

This mismatch specifically affects data handling expectations: the skill is presented as an end-to-end acquisition and verification workflow, but appears to perform a narrower local text-processing role in some paths. Such ambiguity can cause users to supply browser profiles, API keys, or content under false assumptions about what processing actually occurs and where data flows.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
85% confidence
Finding

The documentation advises placing an API key in a shared environment file path, which can broaden secret exposure to other tools, skills, or users on the system. While not direct credential theft logic, encouraging long-lived plaintext secret storage in a common dotfile increases the chance of accidental disclosure, misuse, or exfiltration by unrelated processes.

Content

Scanner excerpt · SKILL.md (reported line 73)May include surrounding context.

对 OpenClaw / Gateway 环境,建议写入:

bash
~/.openclaw/.env

获取 API 密钥: https://cloud.siliconflow.cn/

Tainted flow: 'files' from open (line 193, file read) → requests.post (network output)

High
Category
Data Flow
Confidence
97% confidence
Finding

The code uploads extracted audio to a third-party transcription service together with an API bearer token. This is an external data exfiltration path for potentially sensitive media content, and in the skill context it is more dangerous because the skill description emphasizes in-browser processing rather than explicit off-platform transfer of user data.

Content

Scanner excerpt · scripts/douyin_downloader.py (reported line 199)May include surrounding context.

python
}
        headers = {"Authorization": f"Bearer {self.api_key}"}
        try:
            response = requests.post(self.api_base_url, files=files, headers=headers)
            response.raise_for_status()
            result = response.json()
            if 'text' in result:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill requests or implies powerful capabilities including environment access, file read/write, network, and shell execution, but does not declare an explicit tool scope or permission boundary. That makes operator review and policy enforcement harder, and increases the risk that a user invokes a skill with broader capabilities than expected, especially since it downloads media, uses API keys, and writes local artifacts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill instructs users to run the browser with a persistent profile that stores login state locally, but does not prominently warn about the sensitivity of those artifacts. Persistent browser profiles can contain session cookies and authentication material; if stored insecurely or reused broadly, they may enable account takeover of the logged-in Douyin session.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill explicitly downloads video content and writes transcripts and metadata to local files, but the description does not clearly foreground this as a privacy and storage risk. Local persistence of copyrighted media, speech transcripts, and metadata can create compliance, privacy, and data leakage issues if users are unaware of what is retained.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The publish copy explicitly describes reusing a fixed logged-in browser profile and pausing for manual CAPTCHA completion, but it does not warn users about the privacy and account-security implications. Reusing a persistent authenticated profile can expose session data, cookies, and account context to unintended access or misuse, especially in shared or automated environments.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The file hardcodes an external transcription endpoint, confirming that media-derived content is sent off-host. External transmission is security-relevant here because it moves user data across trust boundaries and, combined with the undeclared scope and lack of warning, creates privacy and compliance risk.

Content

Scanner excerpt · scripts/douyin_downloader.py (reported line 53)May include surrounding context.

python
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/121.0.2277.107 Version/17.0 Mobile/15E148 Safari/604.1'
}

DEFAULT_API_BASE_URL = "https://api.siliconflow.cn/v1/audio/transcriptions"
DEFAULT_MODEL = "FunAudioLLM/SenseVoiceSmall"

Tainted flow: 'share_url' from requests.get (line 77, network input) → requests.get (network output)

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The tool extracts the first URL from arbitrary input and fetches it with requests.get without restricting scheme, hostname, redirect behavior, or destination IPs. In an agent/runtime context, this creates SSRF risk because an attacker can supply a crafted link that causes the host running the skill to make unintended outbound requests, potentially to internal services or cloud metadata endpoints before any Douyin-specific normalization occurs.

Content

Scanner excerpt · scripts/douyin_downloader.py (reported line 74)May include surrounding context.

python
raise ValueError("未找到有效的分享链接")

        share_url = urls[0]
        share_response = requests.get(share_url, headers=HEADERS)
        share_response.raise_for_status()
        video_id = share_response.url.split("?")[0].strip("/").split("/")[-1]
        share_url = f'https://www.iesdouyin.com/share/video/{video_id}'

Tainted flow: 'share_url' from requests.get (line 77, network input) → requests.get (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/douyin_downloader.py (reported line 79)May include surrounding context.

python
video_id = share_response.url.split("?")[0].strip("/").split("/")[-1]
        share_url = f'https://www.iesdouyin.com/share/video/{video_id}'

        response = requests.get(share_url, headers=HEADERS)
        response.raise_for_status()

        pattern = re.compile(r"window\._ROUTER_DATA\s*=\s*(.*?)</script>", flags=re.DOTALL)

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The implementation includes watermark-free video downloading and local persistence, which exceeds the declared skill scope of search, filtering, link retrieval, and transcript cleanup. Scope expansion matters in agent systems because users and platform reviewers may authorize the skill for one class of actions while the code performs additional content acquisition and storage with legal, compliance, and abuse implications.

Content

No source excerpt is available for this finding.

Tainted flow: 'filepath' from os.getenv (line 113, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/douyin_downloader.py (reported line 123)May include surrounding context.

python
total_size = int(response.headers.get('content-length', 0))

        downloaded = 0
        with open(filepath, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code sends audio to an external transcription API, which is materially broader than a description suggesting in-webpage extraction and cleanup. In context, this is security-relevant because it changes the trust boundary: user media leaves the browser/session and is processed by a third party using a secret credential.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Audio content is transmitted to an external service without an explicit warning, consent mechanism, or privacy notice. In this skill's context, that is more dangerous because the expected workflow appears to be local/browser-assisted content handling, so users may not anticipate off-platform transfer of possibly sensitive audio and derived text.

Content

No source excerpt is available for this finding.

Tainted flow: 'transcript_path' from os.getenv (line 289, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/douyin_downloader.py (reported line 290)May include surrounding context.

python
video_folder.mkdir(parents=True, exist_ok=True)

        transcript_path = video_folder / "transcript.md"
        with open(transcript_path, 'w', encoding='utf-8') as f:
            f.write(f"# {video_info['title']}\n\n")
            f.write(f"| 属性 | 值 |\n")
            f.write(f"|------|----|\n")

Tainted flow: 'video_path' from os.getenv (line 273, credential/environment) → shutil.copy2 (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/douyin_downloader.py (reported line 306)May include surrounding context.

python
if save_video:
            saved_video_path = video_folder / f"{video_info['video_id']}.mp4"
            shutil.copy2(video_path, saved_video_path)
            if show_progress:
                print(f"视频已保存到: {saved_video_path}")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/run_pipeline.py (reported line 37)May include surrounding context.

python
def run(cmd: List[str], check: bool = True, input_text: Optional[str] = None) -> str:
    res = subprocess.run(cmd, input=input_text, text=True, capture_output=True)
    if check and res.returncode != 0:
        raise RuntimeError(f"command failed: {' '.join(cmd)}\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}")
    return res.stdout.strip()

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file contains hard-coded Chinese captcha markers and user prompts, and later relies on Chinese UI labels such as 登录, 搜索你感兴趣的内容, 搜索, and 视频. This effectively constrains operation and user interaction to a specific language/locale without opt-in or fallback, which is a natural-language policy issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The description mentions transcript extraction and multi-file output capabilities without clearly warning that the skill writes multiple files containing source-derived transcript content and corrected text. This can lead to accidental retention or disclosure of potentially sensitive or copyrighted material if users do not realize artifacts are persisted to disk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

Natural-language strings in the docstring and command-line help force a single language/locale experience with no opt-in or alternative. This can violate language/locale policy where skills are expected to avoid imposing a language unless clearly justified or user-selectable.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

模块文档把“自动保存文案到文件”写成默认功能,容易让人理解为提取时总会落盘。实际实现中只有 extract_text(..., output_dir=...) 传入输出目录时才会创建文件并写入文案,否则结果仅以内存字典返回。

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

该技能声明的用途是抖音视频搜索、筛选、链接获取和文案提取修正,但文件通过环境变量 API_KEY 获取第三方服务凭据并用于远程调用。对于一个看起来以网页操作为核心的技能,这种凭据读取与外部服务依赖并非从用途描述中可以直接推断。

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.