Back to skill

Security audit

Video Downloader

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent video downloader, but its wrapper passes user-supplied URLs to yt-dlp without URL validation or an end-of-options marker, which can let option-like inputs change what yt-dlp does.

Review before installing. Use only trusted video URLs, avoid passing custom yt-dlp flags unless you understand them, and prefer a version that validates HTTP/HTTPS URLs and inserts '--' before the URL in every yt-dlp command.

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
download_video.py:31
Finding

Untrusted URL Is Interpreted as yt-dlp Command-Line Options

Content
View full analysis
dict: """Fetch metadata without downloading.""" result = subprocess.run( ["yt-dlp", "--dump-single-json", "--no-playlist", url], check=True, text=True, capture_output=True, ) return json.loads(result.stdout) def build_command(args: argparse.Namespace) -> list[str]: """Build the yt-dlp command from parsed arguments.""" output_dir = Path(args.output).expanduser() output_template = str(output_dir / "%(title)s [%(id)s].%(ext)s") cmd = ["yt-dlp", "--no-playlist", "--no-progress"] if args.audio_only: cmd.extend(["-x", "--audio-format", "mp3", "--audio-quality", "0"]) else: cmd.extend(["-f", format_selector(args.quality), "--merge-output-format", args.format]) if args.restrict_filenames: cmd.append("--restrict-filenames") cmd.extend(["-o", output_template, args.url]) return cmd ``` ### Technical Analysis The purported URL is appended directly to two `yt-dlp` command lines without an end-of-options delimiter such as `--`. A value beginning with `-` can therefore be parsed by `yt-dlp` as an option rather than as a media URL. Python's `subprocess.run` is correctly invoked with an argument list and without `shell=True`, so ordinary shell metacharacters do not produce shell injection. However, using a list does not prevent injection into the invoked program's own command-line parser. The wrapper's `argparse` configuration normally rejects an option-like positional value. Nevertheless, a caller can explicitly terminate the wrapper's options and pass such a value as the positional argument: ```bash python3 download_video.py -- '--batch-file=/tmp/attacker-list.txt' ``` The resulting metadata command is effectively: ```bash yt-dlp --d ...[truncated 2668 chars]
Remediation
View remediation
str: parsed = urlparse(value) if parsed.scheme not in {"https", "http"} or not parsed.hostname: raise ValueError("A valid HTTP or HTTPS video URL is required.") return value ``` 3. Explicitly reject values beginning with `-`, even after URL parsing, as defense in depth. 4. Consider adding `--ignore-config` to both yt-dlp invocations if ambient user or system yt-dlp configurations are not required. This prevents unrelated configuration files from silently changing the wrapper's reviewed behavior: ```python ["yt-dlp", "--ignore-config", "--dump-single-json", "--no-playlist", "--", url] ``` 5. Add regression tests covering: - `--batch-file=...` - `--config-locations=...` - `--exec=...` - malformed URLs - unsupported schemes - valid HTTP and HTTPS URLs 6. Continue using argument-list subprocess execution with `shell=False`; do not replace it with a shell command string. ]]>
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • 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
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill clearly instructs use of shell-capable tooling (python3 download_video.py and fallback to raw yt-dlp) but does not declare any explicit tool scope such as permissions or allowed-tools. That creates an enforcement gap: an agent may invoke broader shell access than intended, increasing the risk of command misuse or unauthorized local actions if later prompts, wrapper behavior, or related files are adversarial or unsafe.

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 · download_video.py (reported line 14)May include surrounding context.

python
def ensure_yt_dlp() -> bool:
    """Return True when yt-dlp is available in PATH."""
    try:
        subprocess.run(
            ["yt-dlp", "--version"],
            check=True,
            stdout=subprocess.DEVNULL,

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 · download_video.py (reported line 38)May include surrounding context.

python
def inspect_video(url: str) -> dict:
    """Fetch metadata without downloading."""
    result = subprocess.run(
        ["yt-dlp", "--dump-single-json", "--no-playlist", url],
        check=True,
        text=True,

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 · download_video.py (reported line 141)May include surrounding context.

python
print("Running:", " ".join(command))

    try:
        subprocess.run(command, check=True)
    except subprocess.CalledProcessError as err:
        print(f"Error: download failed with code {err.returncode}.", file=sys.stderr)
        return err.returncode

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

This markdown file instructs the agent to store preferences in a local memory file, which is a file-write operation affecting user data. While the prompt asks for consent to remember behavior, it does not explicitly warn that this will modify a local file or describe that persistence side effect.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.