Back to skill

Security audit

Bilibili & YouTube Watcher

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent video transcript helper, but it uses an external downloader with weak URL scoping and unpinned installation guidance that deserve review before installation.

Review this skill before installing. Use it only with video URLs you trust, avoid enabling browser-cookie access unless you intentionally need it, and prefer installing yt-dlp through a pinned, verified, least-privilege environment rather than the README's system-wide latest-download command.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/get_transcript.py:17
Finding
Bypassable Hostname Allowlist Permits Requests to Attacker-Controlled Hosts## Vulnerability Details **File Location**: `scripts/get_transcript.py`, lines 17-25; unvalidated URL execution occurs at lines 98-115 **Vulnerability Type**: Improper hostname validation **Risk Level**: Medium ### Vulnerable Code ```python def detect_platform(url: str) -> str: """Detect video platform from URL.""" domain = urlparse(url).netloc.lower() if any(d in domain for d in ['youtube.com', 'youtu.be', 'youtube-nocookie.com']): return 'youtube' elif any(d in domain for d in ['bilibili.com', 'b23.tv']): return 'bilibili' else: return 'unknown' ``` The accepted URL is subsequently passed unchanged to `yt-dlp`: ```python if platform == 'bilibili': cmd.extend([ "--add-header", "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "--add-header", "Referer: https://www.bilibili.com/", ]) cmd.append(url) try: result = subprocess.run(cmd, cwd=temp_dir, check=True, capture_output=True) ``` ### Technical Analysis `detect_platform()` checks whether an allowed domain string occurs anywhere in `urlparse(url).netloc`. This is substring matching rather than validation against an exact hostname or a legitimate subdomain boundary. Consequently, attacker-controlled hostnames such as `youtube.com.attacker.example`, `fakebilibili.com`, or `b23.tv.attacker.example` satisfy the check. The URL is then passed directly to `yt-dlp`, causing the dependency to process and potentially contact a host outside the advertised YouTube and Bilibili trust boundary. Using an argument list with `subprocess.run()` prevents conventional shell metacharacter injection in this code path. The vulnerability is instead an outbound destination-validation weakness. Redirect handling performed by `yt-dlp` may also allow subsequent requests to destinations not ...[truncated 1193 chars]
Remediation
## Remediation Suggestions Validate the parsed hostname using exact equality or a dot-delimited subdomain boundary: ```python from urllib.parse import urlparse ALLOWED_HOSTS = { "youtube": ( "youtube.com", "www.youtube.com", "m.youtube.com", "youtu.be", "youtube-nocookie.com", "www.youtube-nocookie.com", ), "bilibili": ( "bilibili.com", "www.bilibili.com", "b23.tv", ), } def host_matches(host: str, allowed: str) -> bool: return host == allowed or host.endswith("." + allowed) def detect_platform(url: str) -> str: parsed = urlparse(url) host = (parsed.hostname or "").lower().rstrip(".") if parsed.scheme != "https": return "unknown" for platform, allowed_hosts in ALLOWED_HOSTS.items(): if any(host_matches(host, allowed) for allowed in allowed_hosts): return platform return "unknown" ``` Additional hardening should include: 1. Reject URLs containing credentials in the authority component. 2. Permit only required schemes, preferably HTTPS. 3. Define whether arbitrary subdomains are necessary; use exact hostnames where possible. 4. Review and restrict redirect behavior. If redirects must be followed, validate every redirect destination against the same allowlist. 5. Add tests for malicious suffix and prefix cases, including `youtube.com.example.org`, `notyoutube.com`, and `b23.tv.example.org`. 6. Consider applying network-level egress restrictions as defense in depth.

T08 · Insecure Dependencies

Note
Location
SKILL.md:12
Finding
Unpinned Executable Dependency Installation Weakens Supply-Chain Integrity## Vulnerability Details **File Location**: `SKILL.md`, line 12; `README.md`, lines 19-28 **Vulnerability Type**: Unpinned third-party executable dependency without integrity verification **Risk Level**: Low ### Vulnerable Code `SKILL.md` declares an unpinned package-manager dependency: ```yaml metadata: {"clawdbot":{"emoji":"📺","requires":{"bins":["yt-dlp"]},"install":[{"id":"brew","kind":"brew","formula":"yt-dlp","bins":["yt-dlp"],"label":"Install yt-dlp (brew)"},{"id":"pip","kind":"pip","package":"yt-dlp","bins":["yt-dlp"],"label":"Install yt-dlp (pip)"}]}} ``` `README.md` recommends unpinned package installation and downloading the mutable latest release: ```bash # macOS brew install yt-dlp # Linux sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp sudo chmod +x /usr/local/bin/yt-dlp # Python pip install yt-dlp ``` ### Technical Analysis The installation procedures do not pin `yt-dlp` to a reviewed version. The direct-download procedure follows a mutable `latest` URL and does not verify a checksum or cryptographic signature before placing the file in `/usr/local/bin` and marking it executable. The documented URL points to the official `yt-dlp` GitHub repository, and the project does not use a `curl | sh` pipeline. Nevertheless, the installed executable can change independently of the audited Skill. Package-manager resolution changes, an upstream account or release compromise, or a malicious future release could therefore alter the code executed by the Skill. ### Attack Path 1. A user follows the installation metadata or README instructions. 2. The package manager resolves the current package version, or the direct download follows the mutable `latest` release URL. 3. No project-controlled version constraint, checksum, or signature confirms that the retrieved artifact is the reviewed build. 4. The retrieved artifact is installed as the `yt-dlp` executable. 5. `scripts/get_transcript. ...[truncated 909 chars]
Remediation
## Remediation Suggestions 1. Pin `yt-dlp` to a specific reviewed version in package metadata and installation instructions. 2. Replace the mutable `latest` URL with a version-specific release URL. 3. Publish the expected SHA-256 digest and verify it before installation: ```bash YT_DLP_VERSION="REVIEWED_VERSION" EXPECTED_SHA256="REVIEWED_SHA256" curl --fail --location \ "https://github.com/yt-dlp/yt-dlp/releases/download/${YT_DLP_VERSION}/yt-dlp" \ --output yt-dlp printf '%s %s\n' "$EXPECTED_SHA256" "yt-dlp" | sha256sum --check - sudo install -m 0755 yt-dlp /usr/local/bin/yt-dlp ``` 4. Where supported, verify upstream release signatures or attestations in addition to checksums. 5. Record dependency versions in a lock file or equivalent reproducible dependency manifest. 6. Establish a controlled update process in which new versions are reviewed, tested, and assigned updated hashes before deployment. 7. Avoid system-wide installation when it is unnecessary; prefer an isolated virtual environment or project-specific executable directory.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (9)

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
`ja`, `es`, `fr` |
| Bilibili | `zh-CN` | `en`, `zh-TW`, `ja` |

## Output Format

```markdown
# Platform: Bilibili
# Language: zh-CN
# URL: https://www.bilibili.com/video/...

[Clean transcript text without timestamps...]
```

## Troubleshooting

### "yt-dlp not found"
Install yt-dlp first (see Installation section).

### "HTTP Error 412" (Bilibili)
Your IP may be rate-limited. Solutions:
1. Use cookies: `yt-dlp --cookies-from-browser chrome "URL"`
2. Use proxy: `export HTTP_PROXY="http://proxy:port"`
3. Wait and retry

### "No subtitles found"
The video may not have subtitles available. Try:
- Check if the video has CC (closed captions)
- Try different language: `--lang en` or `--lang zh-CN`

## License

MIT

## Credits

Adapted from [youtube-watcher](https://clawhub.ai/Michaelgathara/youtube-watcher) by Michael Gathara.
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
brew install yt-dlp

# Linux
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
sudo chmod +x /usr/local/bin/yt-dlp

# Python
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
brew install yt-dlp

# Linux
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
sudo chmod +x /usr/local/bin/yt-dlp

# Python
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill invokes a local Python script that reads files and shells out to `yt-dlp`, which implies shell and network-capable behavior, but the manifest does not declare any tool scope such as `permissions` or `allowed-tools`. That mismatch weakens reviewability and least-privilege controls, making it easier for a skill with executable/network behavior to run with broader-than-expected access.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger set includes generic phrases such as `watch video`, `summarize video`, and `analyze video`, which are broad enough to match many ordinary user requests. In a skill that can launch scripts and perform network access, overbroad activation increases the chance of unintended invocation and unnecessary external fetching.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The script automatically sets transcript language to 'zh-CN' for Bilibili and 'en' for YouTube when the user does not provide a language. This imposes a locale choice based solely on platform rather than offering a neutral default or explicit user opt-in, which matches the language/locale policy violation criteria.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The skill extends beyond passive transcript reading by invoking an external downloader and adding spoofed headers specifically to bypass platform anti-scraping controls. In an agent setting, this increases legal/compliance risk and broadens the attack surface because untrusted input causes outbound requests via a powerful third-party tool with site-specific evasion behavior.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cmd.append(url)
        
        try:
            result = subprocess.run(cmd, cwd=temp_dir, check=True, capture_output=True)
        except subprocess.CalledProcessError as e:
            error_msg = e.stderr.decode()
            if "unavailable" in error_msg.lower() or "not available" in error_msg.lower():
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Low
Confidence
85% confidence
Finding
The documentation states that the skill uses 'zh-CN for Bilibili, en for YouTube' as smart defaults, which imposes a language choice based on platform rather than explicit user preference. This is a natural-language locale policy concern because it preselects language behavior without an opt-in at the point of use.

Static analysis

No suspicious patterns detected.