T09 · Insecure Skill Coding Practices
Error
- Location
- index.js:123
- Finding
- Bearer API Key Can Be Transmitted to an Arbitrary Configured Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `index.js:37-41`, `index.js:123-153`; related configuration instructions at `SKILL.md:61-62` **Vulnerability Type**: Unrestricted credential destination and unsafe endpoint configuration **Risk Level**: High ### Vulnerable Code ```js const CONFIG = { apiUrl: process.env.WATCHDOG_API_URL || "https://api.watch.dog/api/mcp_server.php", apiKey: process.env.WATCHDOG_API_KEY || "", }; ``` ```js async function callRemoteTool(toolName, args = {}) { // Remove undefined args Object.keys(args).forEach((k) => args[k] === undefined && delete args[k]); if (!CONFIG.apiKey) { throw new Error( "WATCHDOG_API_KEY is not configured. " + "Please configure it in the .env file of the skill or as an environment variable.", ); } const response = await fetch(CONFIG.apiUrl, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: `Bearer ${CONFIG.apiKey}`, }, body: JSON.stringify({ jsonrpc: "2.0", id: `req_${Date.now()}`, method: "tools/call", params: { name: toolName, arguments: args }, }), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (data.error) { throw new Error(`[${data.error.code}] ${data.error.message}`); } return data?.result?.content?.[0]?.text ?? "{}"; } ``` The documentation explicitly permits a configurable endpoint: ```env WATCHDOG_API_KEY="sk_live_your_key_here" WATCHDOG_API_URL="api_url_here" | "https://api.watch.dog/api/mcp_server.php" ``` ### Technical Analysis `WATCHDOG_API_URL` is accepted without validating its scheme, hostname, port, or network destination. Every tool request then transmits `WATCHDOG_API_KEY` in an `Authorization: Bearer` header to that endpoint. Consequently, a configuration mistake or attacker-influenced onboarding interacti ...[truncated 1809 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Restrict credential-bearing requests to an explicit hostname allowlist, preferably only `api.watch.dog`. 2. Parse the endpoint with `new URL()` and require: - The `https:` scheme. - An approved hostname. - An approved port, normally `443`. - The expected API path where practical. 3. Reject loopback, link-local, private-network, and non-HTTP destinations unless a separately designed enterprise configuration explicitly requires them. 4. Do not let conversational input silently change the credential destination. Require explicit user confirmation that displays the normalized hostname before saving any custom endpoint. 5. Avoid forwarding authorization headers across unvalidated redirects. Disable redirects or validate every destination before credentials are transmitted. 6. Store credentials through the host platform's secret-management facility instead of prompting an agent to write plaintext secrets to `.env`. 7. Apply least-privilege scopes to Watch.dog API keys and provide clear key-rotation instructions. 8. Add automated tests confirming that HTTP URLs, unapproved hosts, private IP addresses, malformed URLs, and unsafe redirects are rejected. ]]>
