T05 · Unauthorized Access and Privilege Escalation
- Location
playlist_server.py:1375- Finding
Unauthenticated Network Exposure of the Music Library and Paid LLM Operations
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
PlaylistGen appears to do what it claims, but its unauthenticated server can expose your music library and use your AI API account if reachable on a network.
Review before installing. Use this only on a trusted machine, bind it to localhost or firewall the port, do not expose it to LAN/public networks without authentication, protect .env and API keys, and expect music metadata and prompts to be sent to Anthropic or MiniMax. Avoid running start.sh with elevated privileges, and check for port conflicts manually because the script can terminate another process on the chosen port.
playlist_server.py:1375Unauthenticated Network Exposure of the Music Library and Paid LLM Operations
playlist_server.py:581Absolute Filesystem Paths Exposed Through Unauthenticated API Responses
playlist_server.py:1216Stored and DOM-Based Cross-Site Scripting Through Unescaped Music Metadata and Playlist Titles
start.sh:19Startup Script Terminates Unrelated Processes Listening on the Configured Port
SKILL.md:41Unpinned Third-Party Dependencies Installed Without Integrity Verification
The README instructs users to source .env before running the indexer, which imports secrets directly into the shell environment. In agent-assisted or multi-user environments, environment variables can be exposed through shell history, subprocess inheritance, debug output, crash reports, or accidental logging, making credential leakage more plausible than simply storing them in a file.
This scans your music directory and enriches each song with LLM-generated tags. Runs once; safe to re-run (skips already-indexed songs).
source .env
python3 smart_indexer.py --path "$MUSIC_DIR" --llm haiku --key "$ANTHROPIC_API_KEY" --db "$DB_PATH" --workers 1
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.
Verify it's up:
curl -s http://localhost:5678/api/stats | python3 -m json.tool | head -5
Tell the user: "PlaylistGen is running at http://localhost:5678 — open it in a browser to search your library."
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# PlaylistGen Environment Variables
# Copy this file to .env and fill in your values
# ── Required ────────────────────────────────────────────────────────────────
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def _load_dotenv():
"""Load .env from the script's directory into os.environ (no-op if missing)."""
env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '.env')
if not os.path.isfile(env_path):
return
with open(env_path) as f:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def _load_dotenv():
"""Load .env from the script's directory into os.environ (no-op if missing)."""
env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '.env')
if not os.path.isfile(env_path):
return
with open(env_path) as f:
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
print(f"MUSIC_RULES.md not found at {MUSIC_RULES_PATH} — using built-in defaults")
except Exception as e:
print(f"Warning: could not load MUSIC_RULES.md: {e}")
return rules
# ── Catalog vocabulary (pre-built at startup, refreshable) ────────────────────
_catalog_vocab: dict = {}
The script sources .env as shell code using source .env, which does more than load credentials: it executes any commands embedded in that file in the current shell context. If an attacker can modify .env, this becomes arbitrary command execution and can also expose sensitive secrets loaded into the environment.
cd "$SCRIPT_DIR"
# Load environment
if [ -f .env ]; then
set -a; source .env; set +a
else
echo "Warning: .env not found. Using defaults or existing environment."
This finding points to the same risky behavior: loading .env through shell source executes file contents rather than merely reading configuration. In the context of a startup script, that means a tampered .env can run arbitrary commands before the server starts and inherit access to the user's environment and filesystem.
if [ -f .env ]; then
set -a; source .env; set +a
else
echo "Warning: .env not found. Using defaults or existing environment."
fi
# Use venv if present
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.
http://localhost:5678 works for local use; set MUSIC_SERVER_URL to a LAN/Tailscale IP if they want links to work on other devices)cd ~/.openclaw/skills/playlistgen
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
Also check that ffprobe is available:
ffprobe -version
# If missing: sudo apt install ffmpeg (Linux) or brew install ffmpeg (macOS)
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
Also check that ffprobe is available:
ffprobe -version
# If missing: sudo apt install ffmpeg (Linux) or brew install ffmpeg (macOS)
This music server quietly includes external LLM integrations and loads API credentials from environment variables, expanding the trust boundary beyond local playlist serving. In this skill context, that is security-relevant because user prompts and library metadata can be sent to third-party services, creating an unexpected data exfiltration path and increasing attack surface.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import openai # type: ignore
_minimax_client = openai.OpenAI(
api_key=os.environ.get("MINIMAX_API_KEY", ""),
base_url="https://api.minimax.io/v1"
)
HAS_MINIMAX = bool(os.environ.get("MINIMAX_API_KEY", ""))
except Exception:
User-supplied prompts are transmitted to Anthropic or MiniMax without clear disclosure or consent. In this application context, prompts may contain private listening preferences or other free-form text, and the unexpected external transmission creates a real privacy and data-governance risk.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
conn.close()
vocab_path = os.path.join(os.path.dirname(DB_PATH), "music_catalog_vocab.json")
with open(vocab_path, 'w', encoding='utf-8') as f:
json.dump(out, f, indent=2, ensure_ascii=False)
The ffmpeg subprocess is invoked on a path derived from an HTTP request via translate_path(), allowing remote users to trigger server-side processing of arbitrary reachable files. Although shell injection is avoided by using an argument list, this still exposes the service to resource-exhaustion and potentially ffmpeg parser exploitation on attacker-chosen media inputs, which is significant in a network-facing threaded server.
self.send_header('Content-Type', 'audio/mpeg')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
proc = subprocess.Popen(
['ffmpeg', '-i', path, '-f', 'mp3', '-b:a', '192k', '-vn', 'pipe:1'],
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
)
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.
- HARD LIMIT: Never select more than 2 tracks from the same artist
- Maintain genre consistency (or subgenre consistency if the playlist targets a specific subgenre)
- Maximize artist diversity — avoid clustering the same artist back-to-back
- If the prompt implies a specific era (decade or period), prefer songs whose year falls in that range; otherwise spread across eras for variety
- If the prompt implies a specific region or culture, prefer songs from that region; otherwise spread across regions for variety
- If the prompt implies a popularity level (e.g. "obscure", "hits"), apply it as a strong preference; otherwise mix mainstream and niche tracks for variety
- Order for smooth energy and mood transitions"""
No suspicious patterns detected.