Back to skill

Security audit

Spotify Connect

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Spotify playback and account-profile skill; the main cautions are local OAuth token storage and an unpinned Python dependency.

Install this only if you are comfortable letting the skill control Spotify playback for authenticated accounts and store Spotify OAuth tokens under your home directory. Prefer running it on a single-user machine, protect backups of `~/.openclaw/spotify-connect`, revoke the Spotify app if you stop using it, and consider pinning or locking the Spotipy dependency before regular use.

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)

T08 · Insecure Dependencies

Warning
Location
scripts/spotify.py:2
Finding
Unpinned Third-Party Dependency Enables Supply-Chain Code Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/spotify.py`, lines 2-5 **Vulnerability Type**: Unbounded runtime dependency resolution **Risk Level**: Medium ### Vulnerable Code ```python # /// script # requires-python = ">=3.10" # dependencies = ["spotipy>=2.24.0"] # /// ``` The project documentation explicitly instructs users to execute this script through `uv`, which automatically resolves and installs the declared dependency: ```markdown Python dependencies are managed inline via PEP 723 — `uv run` handles installation automatically. No manual `pip install` needed. ``` ### Technical Analysis The dependency constraint `spotipy>=2.24.0` permits any current or future Spotipy release whose version is at least 2.24.0. The project contains no dependency lockfile, exact version constraint, artifact hash, or upper version bound. As a result, the code executed by this Skill is not limited to the dependency version that existed when the Skill was audited. Python packages can execute code during import, and `spotipy` is imported at module initialization. A compromised or malicious future release satisfying this constraint could therefore run code with the privileges of the user invoking the Skill. This is a supply-chain weakness rather than evidence that the current Spotipy package is malicious. ### Attack Path 1. An attacker compromises the upstream Spotipy publishing account, package repository, or release process and publishes a malicious version satisfying `>=2.24.0`. 2. A user invokes a documented command such as: ```bash uv run scripts/spotify.py devices ``` 3. In an environment without a previously fixed resolution, `uv` resolves and installs the attacker-controlled compatible release. 4. Python imports `spotipy` before processing the requested command. 5. Malicious package initialization code executes with the invoking user's operating-system privileges. 6. That code may read the Spotify client credentials from the environmen ...[truncated 890 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the open-ended dependency range with an exact, audited version: ```python # dependencies = ["spotipy==2.24.0"] ``` 2. Generate and commit a `uv.lock` file if the selected execution workflow supports project-level locking. 3. Execute dependency installation and updates in locked or frozen mode so dependency changes require explicit review. 4. Verify package artifacts with cryptographic hashes where the packaging workflow supports hash enforcement. 5. Review transitive dependencies, not only the direct Spotipy dependency. 6. Use automated dependency scanning and controlled update pull requests so version changes are auditable. 7. Test and review dependency upgrades before changing the pinned version. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/spotify.py:85
Finding
OAuth Token Cache Files Are Not Explicitly Permission-Hardened<![CDATA[ ## Vulnerability Details **File Location**: `scripts/spotify.py`, lines 85-91 and 149-156 **Vulnerability Type**: Potentially permissive storage of OAuth access and refresh tokens **Risk Level**: Low ### Vulnerable Code Token-cache configuration for normal client creation: ```python token_file = CONFIG_DIR / acct["token_file"] auth = SpotifyOAuth( client_id=client_id, client_secret=client_secret, redirect_uri="http://127.0.0.1:8888/callback", scope=SCOPES, cache_path=str(token_file), ) return spotipy.Spotify(auth_manager=auth) ``` Token-cache configuration during authentication: ```python auth = SpotifyOAuth( client_id=client_id, client_secret=client_secret, redirect_uri="http://127.0.0.1:8888/callback", scope=SCOPES, cache_path=str(CONFIG_DIR / token_file), ) sp = spotipy.Spotify(auth_manager=auth) user = sp.current_user() ``` The account metadata file is explicitly protected elsewhere: ```python ACCOUNTS_PATH.write_text(json.dumps(accounts, indent=2) + "\n") ACCOUNTS_PATH.chmod(0o600) ``` No equivalent permission enforcement is applied to the more sensitive OAuth token-cache files. ### Technical Analysis Spotipy's `SpotifyOAuth` receives a filesystem cache path and manages OAuth token data at that location. Such token data can include access tokens, refresh tokens, token expiry information, and granted scopes. The application explicitly changes `accounts.json` to mode `0600`, but it does not set restrictive permissions on: - `~/.openclaw/spotify-connect` - Newly created `token_<name>.json` files - Token files rewritten when tokens are refreshed - A migrated legacy `token.json` Consequently, the effective token-file permissions depend on Spotipy's cache implementation and the process umask. On a multi-user host with permissive defaults, token files may be readable by another local user. This is a security-hardening deficiency; actual exploitability depends on the resulting filesystem permissions a ...[truncated 1453 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create and enforce restrictive permissions on the configuration directory: ```python CONFIG_DIR.mkdir(parents=True, exist_ok=True, mode=0o700) CONFIG_DIR.chmod(0o700) ``` 2. Enforce mode `0600` on every token file immediately after authentication and after any operation that may refresh or rewrite a token: ```python if token_file.exists(): token_file.chmod(0o600) ``` 3. Apply the same protection to the legacy `token.json` file during migration. 4. Consider implementing or configuring a custom Spotipy cache handler that writes atomically with mode `0600`, rather than correcting permissions only after a write. 5. Where supported, store refresh tokens in an operating-system credential manager or encrypted secret store instead of a plaintext JSON file. 6. Avoid logging token contents and ensure backup systems preserve restrictive access controls. 7. Add tests that fail if the configuration directory is not `0700` or token files are not `0600` on POSIX systems. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • 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
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill explicitly requires environment variables, reads and writes account/device state under the user's home directory, and instructs users to execute shell commands, but it does not declare any tool restrictions or allowed-tools scope. This increases the blast radius if the skill or its backing script is modified or abused, because an agent may invoke shell and file operations with broader privileges than necessary.

Session Persistence

Medium
Category
Rogue Agent
Content
## Setup (one-time)

1. Create a Spotify app at https://developer.spotify.com/dashboard
   - Set redirect URI to `http://127.0.0.1:8888/callback`
   - Enable "Web API" and "Web Playback SDK"
   - Note the Client ID and Client Secret
Confidence
75% confidence
Finding
The skill stores long-lived authenticated Spotify account data in ~/.openclaw/spotify-connect/accounts.json and describes auto-refreshing tokens, creating session persistence on disk. If that file is readable by other local users, exposed by backups, or mishandled by other tools, an attacker could reuse the refresh token to control the victim's Spotify account until revoked.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest describes a skill for controlling playback on Spotify Connect devices and switching among named profiles, but the code also performs full OAuth account onboarding by reading client credentials from environment variables, initiating authentication, querying the user profile, and persisting account metadata and token cache files locally. That is broader than simple playback control and is not reflected in the manifest description.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This code performs a file deletion via token_file.unlink() and immediately removes local authentication state, but there is no confirmation prompt before the destructive action. Although it prints after deletion, the user is not warned in advance that logout will delete the cached token file from disk.

Description-Behavior Mismatch

Low
Confidence
92% confidence
Finding
The manifest mentions support for multiple Spotify accounts with named profiles, but the code goes further by listing accounts, switching active accounts, and logging accounts out by deleting cached tokens. These are real user-facing behaviors beyond the playback/device actions enumerated in the manifest.