T09 · Insecure Skill Coding Practices
Error
- Location
- key-guard.js:80
- Finding
- Arbitrary Credential Exfiltration and Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `key-guard.js`, lines 80–97 and 143–151 **Vulnerability Type**: Arbitrary secret exfiltration through caller-controlled authenticated requests **Risk Level**: Critical ### Vulnerable Code ```js function getKey(name) { const all = loadAllKeys(); return all[name] || process.env[name] || null; } // ── HTTP helper (no external deps) ─────────────────────────────────────────── function request(url, options = {}) { return new Promise((resolve, reject) => { const parsed = new URL(url); const lib = parsed.protocol === "https:" ? https : http; const req = lib.request( { ...parsed, method: options.method || "GET", headers: options.headers || {} }, (res) => { let body = ""; res.on("data", (chunk) => (body += chunk)); res.on("end", () => { try { resolve({ status: res.statusCode, body: JSON.parse(body) }); } catch { resolve({ status: res.statusCode, body }); } }); } ); req.on("error", reject); if (options.body) req.write(JSON.stringify(options.body)); req.end(); }); } ``` ```js async function call_api({ key_name, url, method = "GET", headers = {}, body }) { const key = getKey(key_name); if (!key) return { error: `Key '${key_name}' not found` }; // Inject key into Authorization header (adapt pattern as needed) const authHeaders = { Authorization: `Bearer ${key}`, ...headers }; try { const result = await request(url, { method, headers: authHeaders, body }); // Return API result — raw key was never sent to Claude return { status: result.status, data: result.body }; } catch (err) { return { error: err.message }; } } ``` ### Technical Analysis The MCP caller controls both `key_name` and `url`. `getKey()` can retrieve values not only from the project's configured key sources, but also from arbitrary variables in `process.env`. The selected value is the ...[truncated 1688 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Replace arbitrary `key_name` and `url` combinations with an explicit configuration mapping each permitted key to exact approved HTTPS origins. - Refuse all HTTP destinations and validate protocol, hostname, port, and path before sending a credential. - Resolve hostnames and reject loopback, private, link-local, multicast, and cloud metadata address ranges for both IPv4 and IPv6. - Disable redirects or validate every redirect destination using the same rules. - Remove arbitrary `process.env[name]` lookup. Expose only credentials explicitly declared in a restrictive configuration. - Require explicit user approval before sending a credential to a new destination. - Apply request timeouts, response-size limits, and outbound network restrictions at the operating-system or container level. - Where possible, implement provider-specific tools rather than a general authenticated HTTP proxy. ]]>
