Back to skill

Security audit

XHS Video Downloader

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Xiaohongshu video downloader, but its bundled script is under-scoped and can fetch arbitrary URLs and write to unsafe output paths.

Review before installing. Use only for videos you are allowed to download, avoid authenticated or private pages unless you have explicit permission, and do not run the bundled script against untrusted URLs or with user-supplied filenames until URL allowlisting and output-path sanitization are added.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/download_video.py:122
Finding
Unrestricted Page URL Fetching Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/download_video.py`, lines 122–125 and 153 **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python def fetch_page_content(page_url: str) -> Optional[str]: try: response = requests.get(page_url, headers=HEADERS, timeout=30) response.raise_for_status() return response.text except requests.RequestException as e: print(f"Failed to fetch page: {e}") return None ``` The function is called directly with the user-supplied command-line argument: ```python html_content = fetch_page_content(args.url) ``` ### Technical Analysis The application performs an HTTP request to a user-controlled URL without validating: - The URL scheme - The destination hostname - The resolved IP address - The destination port - Redirect destinations - Whether the destination belongs to Xiaohongshu Although the Skill is intended to process Xiaohongshu pages, the implementation does not enforce this restriction. The `requests` library also follows redirects by default, so validating only an initial URL in a future partial fix would not be sufficient. An attacker can provide a URL targeting loopback addresses, private network ranges, link-local services, or cloud instance metadata endpoints. DNS rebinding or an attacker-controlled redirect can similarly route a superficially legitimate request to a prohibited destination. ### Attack Path 1. An attacker supplies a URL such as `http://127.0.0.1:PORT/internal`, a private-network service, or a URL that redirects to such a destination. 2. `args.url` is passed directly to `fetch_page_content()`. 3. `requests.get()` connects to the attacker-selected destination from the environment running the Skill. 4. The target response is read and processed as HTML. 5. Observable output, response differences, or subsequent media extraction can disclose service availability or cause further request ...[truncated 644 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only `https` URLs. 2. Apply an explicit hostname allowlist for supported Xiaohongshu domains. 3. Reject embedded credentials, unexpected ports, malformed hostnames, and IP-address literals. 4. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, reserved, and unspecified addresses for both IPv4 and IPv6. 5. Disable automatic redirects or validate every redirect destination using the same policy. 6. Account for DNS rebinding by ensuring that the address validated is the address used for the connection. 7. Apply connection and response-size limits in addition to the existing timeout. 8. Return a clear validation error before making any network request when the URL is outside the approved scope. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/download_video.py:57
Finding
Unvalidated Extracted Media URL Enables Secondary SSRF and Arbitrary Content Retrieval<![CDATA[ ## Vulnerability Details **File Location**: `scripts/download_video.py`, lines 57–64 and 91 **Vulnerability Type**: Server-Side Request Forgery through an extracted media URL **Risk Level**: High ### Vulnerable Code ```python mp4_pattern = r'https?://[^\s"<>]+\.mp4[^\s"<>]*' mp4_matches = re.findall(mp4_pattern, html_content) if mp4_matches: # Prefer xhscdn URLs for url in mp4_matches: if "xhscdn" in url: return url.split('"')[0].split("'")[0] return mp4_matches[0].split('"')[0].split("'")[0] ``` The selected URL is subsequently requested without destination validation: ```python response = requests.get(url, headers=HEADERS, stream=True, timeout=60) ``` ### Technical Analysis The extractor merely prefers URLs containing the substring `xhscdn`; it does not require an approved CDN hostname. If no such match exists, it returns the first arbitrary HTTP or HTTPS URL whose text contains `.mp4`. Substring matching is not a secure hostname check. For example, attacker-controlled hostnames can contain `xhscdn` without belonging to the legitimate CDN. The download request also follows redirects by default, and neither the final destination nor its resolved address is checked. Consequently, any attacker who controls the fetched page content can direct the downloader to another attacker-selected HTTP endpoint. The endpoint does not need to serve an actual video because the downloader does not validate the response content type. ### Attack Path 1. The attacker causes the Skill to fetch attacker-controlled or manipulated HTML. 2. The HTML contains a URL matching the broad MP4 regular expression, such as an internal URL with `.mp4` in its path or query. 3. `extract_video_url()` returns that URL because no strict CDN allowlist is enforced. 4. `download_video()` requests the selected destination from the Agent host. 5. The response body is saved locally as an MP4-named file regardless of its actual content. 6. Redirects can be ...[truncated 590 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse extracted URLs with a standards-compliant URL parser rather than relying on substring checks. 2. Require HTTPS and an exact approved CDN hostname or a properly bounded approved subdomain. 3. Do not use checks such as `"xhscdn" in url`; compare normalized hostnames against an explicit allowlist. 4. Reject URL credentials, unexpected ports, fragments, and malformed hostnames. 5. Resolve and reject private, loopback, link-local, reserved, multicast, and unspecified addresses. 6. Disable redirects or validate every redirect target and resolved destination. 7. Require an expected video media type and reject HTML, JSON, and generic binary responses unless explicitly supported. 8. Enforce a maximum download size and stop the transfer when the limit is exceeded. 9. Write to a temporary file and delete partial or invalid downloads on failure. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/download_video.py:167
Finding
User-Controlled Filename Allows Path Traversal and Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/download_video.py`, lines 167–182, with the file-write sink at line 104 **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: High ### Vulnerable Code ```python if args.filename: filename = args.filename if not filename.endswith(".mp4"): filename += ".mp4" else: # Extract note ID from URL note_id_match = re.search(r"/explore/([a-f0-9]+)", args.url) if note_id_match: filename = f"xiaohongshu_{note_id_match.group(1)}.mp4" else: filename = "xiaohongshu_video.mp4" output_path = output_dir / filename # Download success = download_video(video_url, output_path) ``` The resulting path is opened for writing: ```python with open(output_path, "wb") as f: ``` ### Technical Analysis The `--filename` value is accepted without checking whether it is: - An absolute path - A path containing `..` traversal components - A nested path - A symbolic-link target - A path that escapes the selected output directory In `pathlib`, joining an absolute filename to `output_dir` discards the preceding output directory. Relative values containing traversal components can likewise resolve outside it. Opening the resulting path with mode `"wb"` creates the file if absent and truncates it if it already exists. The `.mp4` suffix check does not prevent traversal. An attacker can target any writable file whose name already ends in `.mp4`, or select an arbitrary path and allow the code to append the suffix. ### Attack Path 1. The attacker supplies or induces execution with a filename such as `../../target.mp4` or an absolute writable path. 2. The value is assigned directly to `filename`. 3. `output_dir / filename` produces a path outside the intended download directory. 4. `download_video()` opens that path using `"wb"`. 5. Existing content is truncated and replaced with the downloaded response, or a new file is created with the process's filesystem ...[truncated 848 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat `--filename` strictly as a basename: - Reject absolute paths. - Reject `.` and `..` components. - Reject directory separators for all supported platforms. 2. Normalize the output directory and candidate path with `resolve()`, then verify that the candidate remains inside the resolved output directory. 3. Generate server-controlled filenames where possible and use the user value only as a sanitized display name. 4. Restrict accepted characters and enforce a reasonable filename-length limit. 5. Use exclusive creation mode to prevent silent overwrites unless an explicit overwrite option is provided. 6. Defend against symbolic-link attacks by using platform-appropriate no-follow and atomic file-creation mechanisms. 7. Download into a securely created temporary file within the approved directory, validate it, and atomically rename it to the final path. 8. Run the Skill with least-privilege filesystem permissions. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill instructs use of browser automation, JavaScript extraction, curl, and a Python downloader to access external sites, but it declares no explicit tool scope or permissions. That mismatch creates an authorization and governance gap: an agent could invoke network-capable actions without clear restriction, review, or least-privilege boundaries.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The skill is designed to download third-party video content and even suggests bypassing access controls like JS rendering and header checks, yet it provides no warning about copyright, terms-of-service, privacy, or account-related implications. In context, this increases the chance of misuse for unauthorized copying of user content or downloading from authenticated pages without informed user consent or policy checks.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The file is explicitly framed as a downloader for Xiaohongshu and repeatedly uses the Chinese platform name, but it does not offer any user-selectable language/locale behavior or explain a justified regional constraint. Under the stated policy, natural-language instructions that force a specific locale without opt-in can be flagged even in code docstrings and CLI descriptions.

Static analysis

No suspicious patterns detected.