T09 · Insecure Skill Coding Practices
Error
- Location
- src/cli/commands/serve-mcp.ts:76
- Finding
- Agent-Controlled Absolute URL Can Exfiltrate Stored API Credentials## Vulnerability Details **File Location**: `src/cli/commands/serve-mcp.ts:76-115`; policy handling occurs at `src/core/mcp-server.ts:219-263` **Vulnerability Type**: Origin validation failure and credential disclosure **Risk Level**: High ### Vulnerable Code ```ts // Build target URL (properly join base + path) let baseUrl = serviceConfig.baseUrl; if (!baseUrl.endsWith('/')) baseUrl += '/'; let reqPath = request.path; if (reqPath.startsWith('/')) reqPath = reqPath.slice(1); const targetUrl = new URL(reqPath, baseUrl); // Build headers const headers: Record<string, string> = { ...request.headers }; // Inject auth if (serviceConfig.auth.type === 'bearer' && serviceConfig.auth.key) { headers['Authorization'] = `Bearer ${serviceConfig.auth.key}`; } else if (serviceConfig.auth.type === 'headers' && serviceConfig.auth.headers) { Object.assign(headers, serviceConfig.auth.headers); } else if (serviceConfig.auth.type === 'hmac' && serviceConfig.auth.apiKey && serviceConfig.auth.apiSecret) { // HMAC signature (MEXC-style) const timestamp = Date.now().toString(); targetUrl.searchParams.set('timestamp', timestamp); // Create signature from query string const queryString = targetUrl.searchParams.toString(); const signature = createHmac('sha256', serviceConfig.auth.apiSecret) .update(queryString) .digest('hex'); targetUrl.searchParams.set('signature', signature); headers['X-MEXC-APIKEY'] = serviceConfig.auth.apiKey; } // Set Content-Type for requests with body if (request.body && !headers['Content-Type'] && !headers['content-type']) { headers['Content-Type'] = 'application/json'; } // Make API request const response = await makeAPIRequest(targetUrl, { ...request, headers }); ``` ### Technical Analysis The MCP caller controls `request.path`. The implementation removes one leading slash and passes ...[truncated 1937 chars]
- Remediation
- ## Remediation Suggestions 1. Require request paths to be relative API paths beginning with exactly one `/`. 2. Reject absolute URLs, protocol-relative values such as `//attacker.example`, embedded credentials, backslashes, and control characters. 3. Resolve the path before adding credentials, then compare the resolved URL against the configured base URL: - Protocol must match. - Hostname must match. - Effective port must match. - Username and password must be empty. 4. Inject credentials only after successful origin validation. 5. Prefer constructing the outbound URL by assigning a validated pathname and query to a copy of the configured base URL instead of resolving arbitrary input. 6. Keep redirects disabled, or independently validate every redirect destination before forwarding credentials. 7. Add regression tests for absolute URLs, protocol-relative URLs, HTTP downgrade attempts, encoded separators, backslashes, embedded credentials, and cross-origin redirects. 8. Consider making policy matching operate on a canonicalized pathname and query only.
