Back to skill

Security audit

Universal Video Downloader

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real video downloader, but it can fetch arbitrary URLs from the agent environment and its promised automatic cleanup is not actually enforced.

Review before installing. This skill does what it claims at a high level, but only use it in a sandboxed environment with restricted outbound network access and disk quotas. Expect downloaded files to remain on disk unless the agent explicitly deletes them, and avoid using it for sensitive/private media or untrusted links until URL validation, file lifecycle cleanup, timeouts, and size limits 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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/download.py:47
Finding

Unrestricted URL Processing Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/download.py, lines 7–11 and 47–65
Vulnerability Type: Server-Side Request Forgery (SSRF) and unsafe command argument handling
Risk Level: Medium

Vulnerable Code

python
def get_formats(url):
    try:
        # Use --no-playlist to avoid downloading entire playlists
        # Use --quiet to reduce noise
        result = subprocess.run(['yt-dlp', '-j', '--no-playlist', url], capture_output=True, text=True)
python
def download_video(url, format_id, output_filename):
    try:
        # Sanitize filename (remove non-alphanumeric except dots/dashes)
        safe_name = re.sub(r'[^a-zA-Z0-9._-]', '_', output_filename)
        if not safe_name.endswith('.mp4'):
            safe_name += '.mp4'
            
        # Download command
        # We try to merge into mp4 for maximum compatibility
        cmd = [
            'yt-dlp', 
            '-f', f'{format_id}+bestaudio/best', 
            '--merge-output-format', 'mp4', 
            '--no-playlist',
            '-o', safe_name, 
            url
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True)

Technical Analysis

The url value is taken directly from sys.argv[2] and passed to the network-capable yt-dlp process without validating its scheme, destination hostname, resolved IP address, or redirect targets. Because yt-dlp supports generic URLs in addition to public video platforms, an attacker can supply URLs that reference services reachable only from the host running the Skill.

No controls reject loopback, private, link-local, reserved, or otherwise non-public IPv4 and IPv6 destinations. Redirects are also not independently validated, so a public URL could redirect the request to an internal service. DNS rebinding may similarly defeat a validation mechanism that checks only the original hostname.

The URL is passed aft ...[truncated 1920 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse the supplied URL before invoking yt-dlp and permit only explicitly supported schemes, preferably https and, if required, http.

  2. Prefer an explicit allowlist of trusted public video-platform domains instead of accepting arbitrary sites.

  3. Resolve the destination hostname and reject every address in loopback, private, link-local, multicast, unspecified, reserved, and other non-public ranges for both IPv4 and IPv6.

  4. Apply the same validation to every redirect destination. Do not rely solely on validation of the original URL.

  5. Protect against DNS rebinding by ensuring that the address actually used for the connection remains an approved public address.

  6. Insert -- immediately before the URL argument so that dash-prefixed values cannot be interpreted as yt-dlp options:

    python
    result = subprocess.run(
        ['yt-dlp', '-j', '--no-playlist', '--', url],
        capture_output=True,
        text=True,
        timeout=60
    )
    

    Apply the same option terminator to the download command.

  7. Add execution timeouts, maximum download-size limits, rate limits, and output quotas to reduce denial-of-service exposure.

  8. Run the downloader in a sandbox with restricted filesystem access and outbound network access limited to approved public destinations.

  9. Validate format_id against the format identifiers returned by the preceding information request rather than accepting an arbitrary value.

Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill promises automatic cleanup, but the cleanup exists only as instructional text in SKILL.md and is not enforced by implementation. If the agent forgets or fails to run rm, downloaded media can persist on disk, causing data retention, privacy exposure, and disk exhaustion risks, especially since content comes from arbitrary external URLs.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill invokes shell-capable commands (python3 scripts/download.py ... and rm) but does not declare any tool scope or allowed-tools restrictions. This weakens containment and reviewability, making it easier for an agent to use broader shell access than the skill's stated purpose requires, especially when handling untrusted user-supplied URLs.

Content

No source excerpt is available for this finding.

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/download.py (reported line 11)May include surrounding context.

python
try:
        # Use --no-playlist to avoid downloading entire playlists
        # Use --quiet to reduce noise
        result = subprocess.run(['yt-dlp', '-j', '--no-playlist', url], capture_output=True, text=True)
        if result.returncode != 0:
            return {"error": result.stderr.strip()}

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The download path invokes yt-dlp via subprocess and writes an MP4 file to disk, but the code provides no confirmation prompt, user-facing print/log message, or docstring/comment disclosing this file creation behavior to the user. Under the code-file criteria, file writes and subprocess execution should have some visible warning or disclosure unless clearly covered elsewhere.

Content

No source excerpt is available for this finding.

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/download.py (reported line 65)May include surrounding context.

python
url
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.returncode != 0:
            return {"error": result.stderr.strip()}

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill metadata promises automatic cleanup, but the implementation leaves downloaded files on disk indefinitely. In an agent environment processing repeated requests, this can accumulate user-supplied content, exhaust disk space, and retain potentially sensitive or policy-restricted media longer than expected.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The inline comments state that automatic deletion is handled here, but the finally block performs no cleanup. This mismatch is dangerous because operators may rely on the comments and assume files are ephemeral, leading to unnoticed retention of downloaded content and gradual resource exhaustion.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.