Back to skill

Security audit

Youtube Music

Security checks for vulnerabilities and agentic risk

Overview

This YouTube Music skill is purpose-related, but it needs Review because its docs overstate capabilities and its scripts contain unsafe shell command construction and weak persistent cache handling.

Install only if you are comfortable with this skill controlling an OpenClaw browser profile for YouTube Music. Use an isolated profile/account, avoid passing untrusted text or URLs to its commands, clear its /tmp caches when needed, and treat playlist, queue, lyrics, and recommendation claims as not reliably implemented until the scripts are fixed and the docs are aligned.

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/ultra-play.js:43
Finding
Shell Command Injection in the Ultra Player<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ultra-play.js:43-47`, `scripts/ultra-play.js:74-80`, and `scripts/ultra-play.js:146-152` **Vulnerability Type**: OS command injection through shell command construction **Risk Level**: High ### Vulnerable Code ```js function saveCache(cache) { try { fastExec(`echo '${JSON.stringify(cache)}' > ${CACHE_FILE}`); } catch (e) {} } ``` ```js if (cached && cached.videoId) { console.log(`⚡ Cache hit: ${cached.videoId}`); const url = `${YOUTUBE_WATCH}${cached.videoId}`; fastExec(`openclaw browser open --targetUrl="${url}"`); console.log(`✅ Played in ${Date.now() - start}ms (CACHED)`); return { videoId: cached.videoId, cached: true, time: Date.now() - start }; } ``` ```js case 'direct': const videoId = args[1]; console.log(`🎵 Direct play: ${videoId}`); fastExec(`openclaw browser open --targetUrl="${YOUTUBE_WATCH}${videoId}"`); console.log(`✅ Playing!`); break; ``` The commands are executed by this wrapper: ```js function fastExec(cmd, ignoreError = true) { try { return execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000 }).trim(); } catch (e) { return ignoreError ? '' : e.message; } } ``` ### Technical Analysis `execSync()` receives a single command string, causing Node.js to invoke a system shell. User-controlled data is inserted directly into that command string. The `direct` command accepts `args[1]` as a video ID without validating its format. Because the value is placed inside a double-quoted shell argument, embedded quote characters, command substitutions, or other shell syntax can terminate or alter the intended argument. The cache-writing operation is also unsafe. Search queries become properties in the cache object and are serialized by `JSON.stringify()`. JSON serialization does not make the result safe for insertion into a single-quoted shell expression. A query containing a single quote can ...[truncated 1423 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove shell-based command construction and invoke the executable with an argument array: ```js const { execFileSync } = require('child_process'); function openBrowser(url) { execFileSync( 'openclaw', ['browser', 'open', `--targetUrl=${url}`], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000 } ); } ``` 2. Validate direct video IDs before use: ```js function validateVideoId(videoId) { if (!/^[A-Za-z0-9_-]{11}$/.test(videoId)) { throw new Error('Invalid YouTube video ID'); } return videoId; } ``` 3. Replace shell commands used for cache operations with Node.js filesystem APIs: ```js const fs = require('fs'); function loadCache() { try { return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); } catch { return {}; } } function saveCache(cache) { fs.writeFileSync( CACHE_FILE, JSON.stringify(cache), { encoding: 'utf8', mode: 0o600 } ); } ``` 4. Use `fs.unlinkSync()` for cache deletion rather than invoking `rm`. 5. Treat cache contents as untrusted input. Validate cached video IDs and URLs again when reading them. 6. Add regression tests covering quotes, shell metacharacters, command substitutions, line breaks, malformed URLs, and poisoned cache content. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/control.js:15
Finding
Shell Command Injection Through Direct URL Handling<![CDATA[ ## Vulnerability Details **File Location**: `scripts/control.js:15-20` and `scripts/control.js:52-60` **Vulnerability Type**: OS command injection through unescaped URL parameters **Risk Level**: High ### Vulnerable Code ```js function browserAction(action, params = {}) { try { const cmd = `openclaw browser ${action} ${Object.entries(params) .map(([k, v]) => `--${k}="${v}"`) .join(' ')}`; return execSync(cmd, { encoding: 'utf8' }); } catch (error) { console.error('Browser action failed:', error.message); throw error; } } ``` ```js async function play(query) { ensureBrowser(); if (query.includes('youtube.com') || query.includes('youtu.be')) { // Direct URL browserAction('open', { targetUrl: query }); } else { // Search and play first result const url = `${YOUTUBE_MUSIC_URL}/search?q=${encodeURIComponent(query + ' song')}`; browserAction('open', { targetUrl: url }); } console.log(`Playing: ${query}`); } ``` ### Technical Analysis `browserAction()` constructs a shell command by concatenating the action, parameter names, and parameter values. It then passes the resulting string to `execSync()`, which executes it through a command shell. The `play()` function considers any query containing the substring `youtube.com` or `youtu.be` to be a direct URL. This is not URL validation and does not ensure that the complete value is a legitimate YouTube URL. An attacker can include one of those substrings in a larger value containing shell syntax. Wrapping a value in double quotes does not provide reliable shell escaping. An embedded double quote can terminate the argument, and shell command substitution can be interpreted in double-quoted strings. Although search queries use `encodeURIComponent()`, the direct-URL branch passes the original untrusted query to the vulnerable command builder. ### Attack Path 1. An attacker invokes `control.js play` with a crafted string containing `youtube. ...[truncated 929 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `execSync()` command strings with `execFileSync()` or `spawnSync()` and an explicit argument array: ```js const { execFileSync } = require('child_process'); function browserAction(action, params = {}) { const allowedActions = new Set(['status', 'start', 'open']); if (!allowedActions.has(action)) { throw new Error('Unsupported browser action'); } const args = ['browser', action]; for (const [key, value] of Object.entries(params)) { args.push(`--${key}=${String(value)}`); } return execFileSync('openclaw', args, { encoding: 'utf8' }); } ``` 2. Parse and validate direct URLs using the standard URL parser: ```js function validateYouTubeUrl(value) { const parsed = new URL(value); const allowedHosts = new Set([ 'music.youtube.com', 'youtube.com', 'www.youtube.com', 'youtu.be' ]); if (parsed.protocol !== 'https:' || !allowedHosts.has(parsed.hostname)) { throw new Error('Unsupported YouTube URL'); } return parsed.toString(); } ``` 3. Do not use substring checks as security validation. 4. Restrict parameter names to a fixed allowlist so future callers cannot inject arbitrary command-line options. 5. Add tests confirming that quotes, separators, command substitutions, malformed URLs, user-info URL tricks, subdomain confusion, and non-HTTPS schemes are rejected or treated purely as data. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ultra-play.js:16
Finding
Predictable Shared Temporary Files Permit Cache Poisoning and Symlink Attacks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ultra-play.js:16, 32-47`; `scripts/youtube-music-v3.sh:9, 51-59, 101-109, 116-119`; `scripts/youtube-music.sh:15, 84-108, 146-149` **Vulnerability Type**: Unsafe predictable temporary-file handling **Risk Level**: Medium ### Vulnerable Code From `scripts/ultra-play.js`: ```js const CACHE_FILE = '/tmp/yt_music_v3_cache.json'; // Load cache function loadCache() { try { const data = fastExec(`cat ${CACHE_FILE}`, false); return data ? JSON.parse(data) : {}; } catch { return {}; } } // Save cache function saveCache(cache) { try { fastExec(`echo '${JSON.stringify(cache)}' > ${CACHE_FILE}`); } catch (e) {} } ``` From `scripts/youtube-music-v3.sh`: ```bash CACHE_FILE="/tmp/yt_music_v3.json" ``` ```bash if [[ -f "$CACHE_FILE" ]]; then local cached=$(grep -o "\"${query,,}\":\"[^\"]*\"" "$CACHE_FILE" 2>/dev/null | cut -d'"' -f4) if [[ -n "$cached" ]]; then echo "$cached" return 0 fi fi ``` ```bash if [[ -f "$CACHE_FILE" ]]; then # Append to existing cache (simplified) echo "{\"${query,,}\":\"pending\"}" >> "${CACHE_FILE}.tmp" else echo "{\"${query,,}\":\"pending\"}" > "$CACHE_FILE" fi ``` From `scripts/youtube-music.sh`: ```bash CACHE_FILE="/tmp/yt_music_cache.json" ``` ```bash if [[ -f "$CACHE_FILE" ]]; then local cached_url=$(grep -o "\"${cache_key}\":\"[^\"]*\"" "$CACHE_FILE" 2>/dev/null | cut -d'"' -f4) if [[ -n "$cached_url" ]]; then log_play "Cached: $query" openclaw browser open --targetUrl="$cached_url" >/dev/null 2>&1 log_success "Playing from cache!" return 0 fi fi ``` ```bash if [[ -f "$CACHE_FILE" ]]; then # Add to cache (simplified) echo "{\"${cache_key}\":\"${search_url}\"}" >> "${CACHE_FILE}.tmp" else echo "{\"${cache_key}\":\"${search_url}\"}" > "$CACHE_FILE" fi ``` ### Technical Analysis The Skill uses fixed, globally predictable filenames under `/tmp`. On typical multi-user syst ...[truncated 2154 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store cache data in a private, user-owned cache directory rather than shared `/tmp`. For example, use `$XDG_CACHE_HOME/youtube-music` or a directory beneath the user's home directory. 2. Create the directory with mode `0700` and cache files with mode `0600`. 3. Use filesystem APIs instead of shell commands. In Node.js, use `fs.readFileSync()`, `fs.writeFileSync()`, `fs.renameSync()`, and `fs.unlinkSync()`. 4. Before reading an existing cache file: - Use `lstat()` rather than following links silently. - Reject symbolic links. - Verify that the file is regular. - Verify that its owner matches the effective user. - Reject unexpectedly permissive modes. 5. Perform atomic writes: - Create a randomly named file in the same private directory. - Open it with exclusive creation. - Write and flush the complete JSON document. - Set restrictive permissions. - Atomically rename it over the destination. 6. For shell implementations, use `mktemp` inside a private directory and install cleanup traps. Do not use globally predictable `.tmp` names. 7. Encode cache content with a real JSON implementation rather than string concatenation and `grep`. 8. Validate all values loaded from cache before using them as URLs, video IDs, command arguments, or browser destinations. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (48)

Self-Modification

High
Category
Rogue Agent
Content
### For Developers:
```bash
# Update skill
cd ~/.openclaw/workspace/skills/youtube-music
git pull  # or manual update
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description claims a functional skill that can control YouTube Music with natural language, including full playback control, search, playlists, and queue management. However, the supplied code chunk is only a demonstration/help script that echoes example commands and documentation paths. Based on this chunk alone, the actual behavior is limited to displaying informational text. That is a material mismatch from the declared operational capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose describes an end-user control skill for YouTube Music with broad playback and playlist functionality. The actual code chunk does not implement those controls; instead, it runs benchmark tests against other local scripts, measures elapsed time, and reports performance metrics. While it is related to YouTube Music tooling, its primary purpose in this chunk is performance benchmarking, which is not reflected in the description. Therefore this is a material description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description overstates the implemented functionality. The actual code's primary purpose is limited browser automation for opening YouTube Music or its search page, plus checking/starting the browser. While this is related to YouTube Music control, most of the declared capabilities are not present: playback controls are placeholders, volume and now-playing are placeholders, and playlist/queue features are entirely absent. The description therefore does not accurately represent what the supplied code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description promises broad YouTube Music control capabilities, including playback actions and playlist/queue management. This code chunk only supports constructing a YouTube Music search URL from command-line input and opening it in a browser. While opening a search page is loosely related to music playback/search, it is materially narrower than the declared purpose. The script also does not demonstrate the commented 'auto-click first result' behavior; it merely opens the search URL. Therefore the description overstates what this code actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description says the skill performs YouTube Music playback and browser automation actions. However, the supplied code chunk does not implement any music-control behavior. Its primary purpose is testing and validating the skill installation/setup: checking file existence, fixing executable bits, verifying browser tooling availability, and validating metadata. These are materially different behaviors from the declared end-user functionality. While such tests can support the skill, this specific code chunk itself does not match the described capability and includes additional setup-related actions like chmod and environment inspection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description overstates the skill's capabilities. The code's primary behavior is limited to opening YouTube Music URLs in a browser for a search query or direct video ID, plus reading/writing a local cache file and exposing cache maintenance commands. While this partially aligns with 'play' and basic search initiation, it does not provide the advertised playback controls such as pause, skip, playlist management, or queue management. The mismatch is material because the declared purpose suggests a comprehensive YouTube Music controller, whereas the actual implementation is a narrow launcher/player helper.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description promises a broad YouTube Music control skill with full playback control, including pause, skip, playlist operations, and queue management. The supplied code only opens YouTube Music search or watch URLs in a browser, with commands for play, play-fast, direct play by video ID, cache stats, and cache clearing. There is no code for interacting with playback controls after page load, no playlist or queue manipulation, and no pause/skip commands. While browser automation is used at a basic level to open URLs, the actual behavior is materially narrower than the declared purpose, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code generally aligns with the broad theme of controlling YouTube Music via browser automation, especially for opening searches and initiating playback attempts. However, the declared description materially overstates the implemented functionality. The script can ensure a browser is running, open YouTube Music search URLs, and maintain a simple local cache of search URLs. In contrast, core advertised features such as full playback control, playlist management, and queue management are absent. Several commands like pause, skip, previous, volume, and now-playing only print messages and do not automate browser interactions. This is not an undeclared malicious capability; rather, it is a description-to-behavior mismatch because the implementation is much narrower and less complete than claimed.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The feature list states that the skill already supports queue management, playlist management, lyrics display, and recommendations. Later in the same document, those same capabilities are listed under 'Future Enhancements,' indicating they are not actually implemented yet. This is an active documentation contradiction about the skill's current behavior and scope.

Session Persistence

Medium
Category
Rogue Agent
Content
- 🔊 **Volume control** - Set volume levels (0-100%)
- 🔍 **Smart search** - Find tracks, artists, albums, playlists
- 📋 **Queue management** - View and manage playback queue
- 💾 **Playlist support** - Create and manage playlists
- 📝 **Lyrics display** - Show lyrics for current track
- 🎯 **Recommendations** - Get suggestions based on current track
Confidence
72% confidence
Finding
The skill is designed to operate through a persistent browser profile ('openclaw') against YouTube Music, which implies session persistence across invocations. Persistent authenticated sessions are not inherently unsafe, but they raise risk because unintended or unauthorized prompts could manipulate the user's account state, playback context, or saved content without re-authentication.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The natural-language examples are very broad and resemble ordinary conversational phrases such as 'pause the music' or 'what's playing now?'. In an auto-invoked agent environment, overly generic triggers can cause the skill to activate unintentionally from unrelated conversation, leading to unintended browser actions and control of a live authenticated YouTube Music session.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The combination of auto-discovery and generic commands like 'play some Bollywood hits' or 'turn it up to 80' provides weak trigger constraints. This increases the chance of accidental or prompt-injected activation, especially because the skill performs browser automation against a persistent logged-in media session.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The log documents automatic browser opening, direct navigation to search-result URLs, auto-play behavior, and persistent local caching without any indication of user consent, notice, or controls. In a browser-automation skill, silently performing actions and retaining query history can expose user behavior, surprise users, and create privacy and transparency risks, especially when media playback is triggered automatically.

Session Persistence

Medium
Category
Rogue Agent
Content
- 🔊 **Volume control** - Set volume levels, mute/unmute
- 📋 **Queue management** - View and manage playback queue
- 🎶 **Search** - Find tracks, artists, albums, playlists
- 💾 **Playlists** - Create and manage playlists
- 📝 **Lyrics** - Display lyrics for current track
- 🎯 **Smart recommendations** - Get suggestions based on current track
Confidence
81% confidence
Finding
The README describes features like playlist creation/management, queue management, recommendations based on the current track, and use of a persistent browser profile, all of which imply interaction with a stateful authenticated session. In this context, session persistence increases the blast radius of accidental or unauthorized commands because actions may affect the user's long-lived account state beyond the current session.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The README advertises very broad natural-language triggers such as "pause the music", "skip to next track", and "search for Arijit Singh hits" without defining activation boundaries, confirmation requirements, or scoping to this specific skill. In an agent environment, overly generic phrases can cause unintended invocation or action overlap with other skills, leading to surprise browser automation and playback changes.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The examples "pause" and "skip" are especially generic and can easily collide with unrelated assistant contexts or other media controls. Because this skill performs browser automation against a live session, ambiguous activation can directly alter user state without clear intent.

Lp3

Medium
Category
MCP Least Privilege
Confidence
77% confidence
Finding
The skill metadata declares browser-driven behavior and even notes a Node dependency, but it does not define any explicit tool scope such as allowed tools or permissions. In an agent ecosystem, missing scope boundaries can let the runtime infer or grant broader shell/code execution capability than users expect, increasing the blast radius if the skill is expanded or invoked unsafely.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The skill omits clear warnings that browser automation will act within the user's logged-in YouTube Music session, potentially affecting playlists, likes, queue state, and listening history. Lack of disclosure undermines informed consent and can lead users to expose account-linked activity or modify personal data unintentionally.

Vague Triggers

Medium
Confidence
92% confidence
Finding
Overly broad trigger phrases such as 'stop', 'continue', 'back', or 'find' can be activated during ordinary conversation and may cause unintended browser actions on the user's YouTube Music account. In an agent setting, ambiguous triggers raise the chance of accidental command execution, especially when the skill can manipulate authenticated sessions and playback state.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The document explicitly promotes persistent query-to-ID caching and cache-clearing commands but does not warn users that this behavior stores data across sessions on disk and may retain listening history or user-derived preferences. In a browser automation skill, persistent local state can surprise users, create privacy exposure on shared systems, and lead to unintended data loss when clearing cache.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The natural-language examples use broad conversational phrases such as 'pause the music' or 'add this to my workout playlist' that could be matched unintentionally in ordinary chat. Because this skill controls a browser session and media actions, overly broad triggers increase the risk of accidental invocation, unintended state changes, and action execution without sufficiently explicit user intent.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The usage documentation advertises natural-language capabilities like lyrics display, playlist modification, queue management, and similarity playback that are not reflected in the documented implemented command set. In an agent skill, this mismatch is dangerous because users or orchestration layers may assume these actions are supported and trigger unintended browser automation, ambiguous fallbacks, or unsafe handler behavior when unsupported intents are inferred.

Session Persistence

Medium
Category
Rogue Agent
Content
## Advanced Examples

### Create a Morning Playlist
```bash
# Queue up morning vibes
./scripts/youtube-music.sh play "morning chill playlist"
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
browserAction() constructs a shell command string and executes it with execSync, interpolating parameter values into the command line. Even though some callers use encodeURIComponent for search URLs, the direct URL path in play() can pass attacker-controlled content into shell execution, creating command-injection risk and making browser control depend on unsafe shell string composition.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/control.js:20

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/direct-play.js:34

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/ultra-play.js:23