Back to skill

Security audit

抖音视频下载器

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but its credential handling and download boundaries create review-worthy risks before installation.

Review before installing. Use only links you have permission to download, prefer an environment variable over CLI/config-file keys, assume submitted URLs are sent to redfox.hk, and avoid using a valuable personal API key until the session header reuse and URL/download validation issues are fixed.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
assets/downloader.py:261
Finding
API Key Disclosure to API-Controlled Download Hosts## Vulnerability Details **File Location**: `assets/downloader.py:105-126`, `assets/downloader.py:261-270`, and `assets/downloader.py:310-345` **Vulnerability Type**: Credential disclosure across trust boundaries **Risk Level**: High ### Vulnerable Code ```python def download_file(session, url, filepath, desc="Downloading"): """Download a file with progress display.""" try: resp = session.get(url, stream=True, timeout=120) resp.raise_for_status() total = int(resp.headers.get("content-length", 0)) downloaded = 0 with open(filepath, "wb") as f: for chunk in resp.iter_content(chunk_size=8192): if chunk: f.write(chunk) downloaded += len(chunk) if total > 0: pct = int(downloaded * 100 / total) bar = "█" * (pct // 5) + "░" * (20 - pct // 5) print(f"\r {bar} {pct}%", end="", flush=True) print() return True except requests.exceptions.RequestException as e: error(f"Download failed: {e}") return False ``` ```python session = requests.Session() session.headers.update({ "Content-Type": "application/json", "X-API-KEY": api_key, }) try: resp = session.post(API_URL, json={"url": url, "source": "短视频下载器-ClawHub"}, timeout=30) result = resp.json() ``` ```python if aweme_type == "video": video_url = data.get("videoUrl") if not video_url: error("API did not return video URL") sys.exit(1) safe_title = sanitize_filename(title) or f"video_{platform}" filename = f"{safe_title}.mp4" filepath = os.path.join(output_dir, filename) info(f"Type: Video") step("Downloading video...") if download_file(session, video_url, filepath): downloaded_files.append(filepath) elif aweme_type == "photo": image_urls = data.get("imageUrls") or [] if not image_urls: err ...[truncated 2478 chars]
Remediation
## Remediation Suggestions - Do not store authentication headers in a session reused for untrusted downloads. - Attach the API key only to the parsing request: ```python api_session = requests.Session() resp = api_session.post( API_URL, headers={ "Content-Type": "application/json", "X-API-KEY": api_key, }, json={"url": url, "source": "video-downloader"}, timeout=30, ) ``` - Use a separate unauthenticated session for media downloads: ```python download_session = requests.Session() download_file(download_session, video_url, filepath) ``` - Parse every returned media URL with `urllib.parse.urlsplit`. - Require HTTPS and reject embedded credentials, malformed hostnames, and unexpected schemes. - Where the service contract permits it, enforce an explicit allowlist of trusted media or CDN hosts. - Ensure authentication headers are not forwarded through redirects to a different origin. - Rotate potentially exposed user keys after deploying the fix.

T09 · Insecure Skill Coding Practices

Warning
Location
assets/downloader.py:30
Finding
Hardcoded Shared API Credential in Source Code## Vulnerability Details **File Location**: `assets/downloader.py:30` and `assets/downloader.py:64-82` **Vulnerability Type**: Hardcoded credential **Risk Level**: Medium ### Vulnerable Code ```python ENV_KEY = "REDFOX_API_KEY" PUBLIC_API_KEY = "ak_b45b6a6881f4400fb321428947eb6661" ``` ```python def get_api_key(cli_key=None): """Get API key with priority: CLI arg > env var > config file.""" if cli_key: return cli_key env_key = os.environ.get(ENV_KEY) if env_key: return env_key if CONFIG_FILE.exists(): try: data = json.loads(CONFIG_FILE.read_text()) key = data.get("api_key") if key: return key except (json.JSONDecodeError, OSError): pass return PUBLIC_API_KEY ``` ### Technical Analysis A functional-looking shared API key is embedded directly in the distributed source and is automatically selected when no user credential is available. Any person who can download or inspect the project can recover and reuse the credential independently of the Skill. Even if intended as a public or anonymous-access key, embedding a reusable credential prevents meaningful confidentiality, rotation isolation, and attribution. It also conflicts with the security guidance in `README.en.md`, which states that keys should never be hardcoded or exposed in code. ### Attack Path 1. An attacker downloads or browses the project. 2. The attacker reads `PUBLIC_API_KEY` from `assets/downloader.py`. 3. The attacker sends requests directly to the RedFox API using that key. 4. Requests consume the shared credential's quota or exercise any permissions assigned to it. 5. Revocation or rate limiting affects every legitimate installation relying on the same fallback key. ### Impact Assessment The attacker obtains the service privileges assigned to the embedded key. Likely consequences include unauthorized API consumption, quota exhaustion, rate-limit depletion, abuse attr ...[truncated 235 chars]
Remediation
## Remediation Suggestions - Immediately revoke or rotate the embedded key. - Remove `PUBLIC_API_KEY` and fail safely when no user credential is configured. - Require a key through a protected environment variable or permission-restricted configuration file. - If anonymous access is required, implement it through a server-controlled mechanism with strict per-client rate limits and narrowly scoped, short-lived tokens. - Add secret scanning to CI and pre-commit workflows. - Reconcile the implementation with the README security policy so documentation and runtime behavior agree.

T09 · Insecure Skill Coding Practices

Warning
Location
assets/downloader.py:124
Finding
Supported-Platform URL Validation Can Be Bypassed## Vulnerability Details **File Location**: `assets/downloader.py:124-173` **Vulnerability Type**: Improper URL and hostname validation **Risk Level**: Medium ### Vulnerable Code ```python def is_valid_share_url(url): """Check if the URL is a valid sharing link from supported platforms. Returns (is_valid: bool, platform_name: str or None). """ # Domain -> Platform mapping for known sharing link patterns platform_patterns = [ # 抖音 Douyin (r'(https?://)?(www\.)?v\.douyin\.com/', '抖音'), (r'(https?://)?(www\.)?douyin\.com/(video|jingxuan|note)/', '抖音'), (r'(https?://)?(www\.)?douyin\.com\/user\/', '抖音'), # 小红书 Xiaohongshu (r'(https?://)?(www\.)?xhslink\.(com|cn)/', '小红书'), (r'(https?://)?(www\.)?xiaohongshu\.com/', '小红书'), (r'(https?://)?(www\.)?xhslink\.com/', '小红书'), # 快手 Kuaishou (r'(https?://)?(www\.)?v\.kuaishou\.com/', '快手'), (r'(https?://)?(www\.)?kuaishou\.com/', '快手'), # 视频号 WeChat Channels (r'(https?://)?weixin\.qq\.com/sph/', '视频号'), # B站 Bilibili (r'(https?://)?(www\.)?b23\.tv/', 'B站'), (r'(https?://)?(www\.)?bilibili\.com/video/', 'B站'), # YouTube (r'(https?://)?(www\.)?youtu\.be/', 'YouTube'), (r'(https?://)?(www\.)?youtube\.com/watch\?', 'YouTube'), (r'(https?://)?(www\.)?youtube\.com/shorts/', 'YouTube'), # Instagram (r'(https?://)?(www\.)?instagram\.com/p/', 'Instagram'), (r'(https?://)?(www\.)?instagram\.com/reel/', 'Instagram'), # X / Twitter (r'(https?://)?(www\.)?x\.com/\w+/status/', 'X (Twitter)'), (r'(https?://)?(www\.)?twitter\.com/\w+/status/', 'X (Twitter)'), # TikTok (r'(https?://)?(www\.)?tiktok\.com/@', 'TikTok'), # Threads (r'(https?://)?(www\.)?threads\.net/@', 'Threads'), # Facebook (r'(https?://)?(www\.)?facebook\.com/.*/videos/', 'Facebook'), (r'(http ...[truncated 2019 chars]
Remediation
## Remediation Suggestions - Parse URLs with `urllib.parse.urlsplit` instead of applying regular expressions to the complete string. - Require `https` unless a specific supported service demonstrably requires HTTP. - Normalize the hostname by lowercasing it, removing a final dot, and safely handling internationalized domain names. - Compare the parsed hostname against an explicit set of exact approved hosts. - If subdomains are allowed, require either exact equality or a dot-delimited suffix, such as `host == domain or host.endswith("." + domain)`. - Reject URLs containing usernames or passwords. - Validate platform-specific paths and query parameters separately after hostname validation. - Repeat validation server-side; client-side checks must not be the sole security boundary. - Add regression tests for query-string embedding, deceptive subdomains, user-information tricks, fragments, mixed case, and malformed URLs.

T09 · Insecure Skill Coding Practices

Warning
Location
assets/downloader.py:105
Finding
Unbounded Remote Downloads Can Exhaust Local Storage## Vulnerability Details **File Location**: `assets/downloader.py:105-126` **Vulnerability Type**: Unrestricted resource consumption **Risk Level**: Medium ### Vulnerable Code ```python def download_file(session, url, filepath, desc="Downloading"): """Download a file with progress display.""" try: resp = session.get(url, stream=True, timeout=120) resp.raise_for_status() total = int(resp.headers.get("content-length", 0)) downloaded = 0 with open(filepath, "wb") as f: for chunk in resp.iter_content(chunk_size=8192): if chunk: f.write(chunk) downloaded += len(chunk) if total > 0: pct = int(downloaded * 100 / total) bar = "█" * (pct // 5) + "░" * (20 - pct // 5) print(f"\r {bar} {pct}%", end="", flush=True) print() return True except requests.exceptions.RequestException as e: error(f"Download failed: {e}") return False ``` ### Technical Analysis The downloader streams remote content directly to disk without imposing a maximum file size. `Content-Length` is used only to render progress and is neither required nor enforced. A server can omit the header, provide a false value, use chunked transfer encoding, or continuously send data. The 120-second timeout does not impose an overall download deadline. In `requests`, it primarily limits periods of socket inactivity. A server that sends data periodically can keep the operation active while the file continues to grow. The function also does not validate `Content-Type` and does not remove partial files when a request fails. ### Attack Path 1. The parsing API returns a media URL controlled by an attacker or pointing to an excessively large resource. 2. The downloader opens the destination file for writing. 3. The remote host omits or falsifies `Content-Length` and continu ...[truncated 755 chars]
Remediation
## Remediation Suggestions - Define a conservative, configurable maximum download size. - Reject a response immediately when a valid `Content-Length` exceeds that maximum. - Independently count streamed bytes and abort once the limit is exceeded: ```python max_bytes = 2 * 1024 * 1024 * 1024 downloaded = 0 for chunk in resp.iter_content(chunk_size=8192): if not chunk: continue downloaded += len(chunk) if downloaded > max_bytes: raise ValueError("Download exceeds the configured size limit") f.write(chunk) ``` - Validate the response `Content-Type` against expected video or image media types. - Use separate connection and read timeouts and enforce an application-level total deadline. - Check available disk space before beginning large downloads. - Download to a securely created temporary file and atomically rename it only after successful completion. - Delete partial files whenever validation, size enforcement, or network transfer fails. - Limit the number of images accepted from a single API response.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Hidden Instructions

High
Category
Prompt Injection
Content
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>短视频下载器 Skill — 审核演示</title>

    <!-- Fonts: Cormorant (display serif) + IBM Plex Sans (body) -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Cormorant:ital,wght@0,400;0,600;1,400&family=IBM+Plex+Sans:wght@300;400;500&display=swap" rel="stylesheet">
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Missing User Warnings

High
Confidence
97% confidence
Finding
The presentation encourages users to paste share links into a workflow backed by a third-party API but omits any privacy warning that submitted URLs and associated metadata are sent off-platform. In this skill context, links can reveal viewing interests, creator identities, and potentially tracking parameters, so silent transmission creates a meaningful privacy and consent risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill prominently advertises watermark-free downloading and remixing across many platforms but omits warnings about copyright, consent, privacy, and platform terms. Because the tool is explicitly designed to retrieve third-party media and remove watermarks, the lack of risk disclosures makes misuse more likely and normalizes potentially unlawful or policy-violating behavior.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The README instructs users to 'just describe what you want in natural language — no commands to memorize,' which is broad enough to trigger the skill from loosely related requests without clear user intent boundaries. In an agent setting, overly permissive invocation guidance can cause unintended activation and automatic downloading of third-party content, increasing the chance of policy, copyright, or privacy violations.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill promotes downloading and saving watermark-free third-party content from multiple social platforms, but it does not warn users about legal, copyright, privacy, or terms-of-service risks. Because the context is mass retrieval and local preservation of external content for reuse, the missing warning materially increases the likelihood of unsafe or non-compliant use.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The README instructs users to issue broad natural-language requests like 'download this video' without defining clear scope, authorization checks, or boundaries on what content may be fetched and stored. In a skill that downloads third-party media from many external platforms, this ambiguity increases the chance of misuse, overbroad triggering, and downloading content the user may not have rights or intent to retrieve.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents execution of a Python downloader that uses network access, reads environment variables for API keys, and writes downloaded media to local storage, but it declares no explicit tool scope or permissions. This creates a transparency and policy gap: hosts and users cannot accurately constrain or review what the skill is allowed to do, increasing the chance of overbroad execution and unsafe deployment.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill sends user-supplied links to the third-party service redfox.hk for parsing, but the description does not clearly warn users that their submitted URLs and related metadata leave the local environment. Shared links can contain personal, private, or tracking information, so silent transmission to an external service creates a privacy and data-handling risk.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill instructs users to pass API keys via command-line arguments and store them in plaintext files without warning about shell history, process-list exposure, and filesystem permissions. These practices can leak credentials to other local users, logs, terminal history, backups, or monitoring tools, leading to unauthorized use of the third-party API account.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file’s description and nearly all CLI messages are hard-coded in Chinese, including usage text and runtime status/error output. This creates a natural-language locale policy issue because the skill does not provide user opt-in, fallback, or any indication that it is intentionally limited to Chinese-speaking users.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The script retrieves an API key from CLI, environment, or a local config file and can persist it to disk, which introduces credential-handling risk beyond a simple downloader’s core function. While the file is chmod 600, storing secrets locally still increases exposure to accidental disclosure, backup leakage, or misuse of a bundled public/default key tied to a third-party service.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest describes a downloader supporting many major platforms including 视频号, YouTube, and Instagram, but this file repeatedly presents the skill as covering only four domestic platforms: 抖音、小红书、快手、B站. The slide deck therefore materially understates or contradicts the manifested scope rather than describing the same capability set.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This HTML file is a markdown-like skill description artifact, so SQP-2 applies to user-facing descriptions. The presentation repeatedly advertises immediate downloading and local saving behavior, but does not warn users that the skill will write files to local storage or where content will be stored.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The slide content advertises '下载过程实时显示进度条和百分比', implying media-download progress tracking. However, the only progress logic in the script is a presentation progress bar based on current slide index, not any file download or percentage reporting.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The presentation discusses API key configuration, including local storage and persistent configuration, without warning users about credential-handling risks. In this context, users may place secrets in config files, CLI history, or shared environments, increasing the chance of accidental disclosure or reuse by other local processes.

Description-Behavior Mismatch

Low
Confidence
79% confidence
Finding
The manifest presents the skill as a downloader for major platforms, which suggests the skill itself performs the necessary retrieval logic. In practice, the implementation sends the user-supplied URL to redfox.hk for parsing and relies on that external service to obtain the media URLs, which is a materially different operational model from a self-contained downloader.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
SQP-3 applies to all file types and covers language/locale policy violations. This file forces a Chinese locale in both metadata and all user-facing content, with no indication that users can choose another language or that the skill is intentionally region-specific.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
assets/downloader.py:30