T09 · Insecure Skill Coding Practices
- Location
scripts/auth.mjs:91- Finding
Bearer API Key Stored Without Explicit Restrictive File Permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears purpose-built for Spotify playlist management, but it warrants review because it stores a Spotify-connected bearer key locally and exposes playlist deletion without clear confirmation guidance.
Review before installing if you care about protecting Spotify playlist access. Treat config/auth.json as a secret, avoid committing or sharing it, and require a clear user confirmation before running delete_playlist. Also note that generated playlists are documented as public on Spotify.
scripts/auth.mjs:91Bearer API Key Stored Without Explicit Restrictive File Permissions
scripts/auth.mjs:61Unescaped OAuth and Upstream Error Data Reflected into Local HTML Responses
${error}
`); console.error(`\nAuth failed: ${error}`); ``` Token-exchange error reflection at lines 107-112: ```js } catch (err) { res.writeHead(500, { "Content-Type": "text/html" }); res.end(`${err.message}
`); console.error(`\nToken exchange error: ${err.message}`); } ``` ### Technical Analysis The callback handler places the `error` query parameter directly into an HTML response without contextual escaping. The token-exchange failure path similarly inserts `err.message`, which may include response content returned by the remote token endpoint. Because the response is declared as `text/html`, attacker-controlled markup can be interpreted by the browser. A payload containing HTML or script could therefore execute in the temporary localhost origin. The exposure is reduced because the server: - Binds only to `127.0.0.1`. - Uses a randomly selected port. - Exists only during the authentication flow. - Closes after handling the callback. These constraints make the issue lower risk, but they do not make unescaped insertion into HTML safe. ### Attack Path One applicable callback path is: 1. The authentication helper starts its temporary loopback HTTP server. 2. An attacker or compromised authorization component causes the browser to request the callback URL with an attacker-controlled `error` query value. 3. The callback handler interpolates that value into the HTML response without escaping it. 4. The browser parses the value as active HTML rather than plain text. 5. Attacker-cont ...[truncated 1008 chars]Referenced artifact was not completely inspected
`https://mcp.playlistable.io` exposes tools via Streamable HTTP transport. The `mcp-call.mjs` script sends JSON-RPC requests directly — no MCP SDK needed.
The skill documents use of environment variables and outbound network access, but it does not declare any explicit tool scope or permission boundaries. That creates an authorization transparency gap: an agent or reviewer cannot easily verify what external access the skill is expected to use, increasing the risk of over-privileged execution or unnoticed data egress.
The authentication instructions state that an API key is automatically saved to config/auth.json, but they do not warn about local secret storage, file permissions, or the sensitivity of the credential. This can lead to inadvertent exposure through weak filesystem permissions, accidental commits, or multi-user environments.
The skill exposes a delete_playlist action as a normal example without warning that it is destructive or instructing the agent to require explicit user confirmation first. In an agent setting, this increases the chance of accidental or prompt-induced deletion of user data without adequate consent.
The API reference documents a destructive delete_playlist operation that removes playlists from both Playlistable and Spotify, but it does not include any warning about irreversible impact or a recommendation for explicit user confirmation before use. In an agent context, this increases the risk of accidental or unauthorized destructive actions being taken on behalf of a user through ambiguous prompts, automation mistakes, or prompt injection influencing tool selection.
Detected: suspicious.dangerous_exec