T09 · Insecure Skill Coding Practices
Warning
- Location
- src/index.js:2492
- Finding
- Unauthenticated Remote MCP Clients Can Consume the Operator's Graph API Quota<![CDATA[ ## Vulnerability Details **File Location**: `src/index.js:2431-2478`, `src/index.js:2492-2515`, and `src/index.js:2654-2670` **Vulnerability Type**: Missing authentication and authorization on credential-bearing MCP endpoints **Risk Level**: Medium ### Vulnerable Code ```js const HANDLERS = { search_subgraphs: searchSubgraphs, recommend_subgraph: recommendSubgraph, get_subgraph_detail: getSubgraphDetail, list_registry_stats: listRegistryStats, semantic_search_subgraphs: semanticSearchSubgraphs, get_schema_changes: getSchemaChanges, execute_query: executeQuery, execute_query_by_subgraph_id: executeQuery, execute_query_by_deployment_id: executeQuery, execute_query_by_ipfs_hash: executeQuery, get_schema: getSchema, get_schema_by_subgraph_id: getSchema, get_schema_by_deployment_id: getSchema, get_schema_by_ipfs_hash: getSchema, get_top_subgraph_deployments: getTopSubgraphDeployments, get_deployment_30day_query_counts: getDeployment30dayQueryCounts, }; function createServer() { const server = new Server( { name: "subgraph-registry", version: PKG_VERSION }, { capabilities: { tools: {} } } ); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: listableTools(), })); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const handler = HANDLERS[name]; if (!handler) { return { content: [{ type: "text", text: JSON.stringify({ error: `Unknown tool: ${name}` }) }], isError: true, }; } try { const result = await handler(args || {}); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (err) { return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }], isError: true, }; } }); return server; } ``` ```js function startHttpTransport(port) { const app = e ...[truncated 4560 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Require authentication on both MCP endpoints** - Authenticate requests to `/sse` before creating a session. - Authenticate every request to `/messages`; do not rely only on possession of a session ID. - Use a securely generated service token, mTLS, or an authenticated reverse proxy. - Compare bearer tokens using a timing-safe comparison. 2. **Add authorization at tool dispatch** - Assign permissions to each authenticated principal. - Explicitly restrict `execute_query*` and live `get_schema*` operations. - Do not treat omission from `tools/list` as an authorization boundary. - Reject direct calls to tools that the current principal is not permitted to use. 3. **Fail safely for keyed non-loopback deployments** - Refuse startup when a Studio API key is configured and the HTTP server binds to a non-loopback interface unless an explicit authentication configuration is present. - Replace the current warning-only behavior with a fatal configuration error. - If an override is necessary, require an explicit option such as `ALLOW_UNAUTHENTICATED_KEYED_HTTP=1` and prominently document its consequences. 4. **Separate public discovery from credentialed execution** - Run the public discovery service without a Studio API key. - Place credentialed execution in a separate loopback-only or private-network process. - Avoid sharing the same unauthenticated transport and handler registry between public discovery and credential-bearing operations. 5. **Implement abuse controls** - Add per-client rate limits, request concurrency limits, and session limits. - Set session expiration and remove idle sessions. - Restrict GraphQL query complexity, depth, aliases, and requested result sizes where practical. - Record authenticated principal, tool name, target identifier, status, and quota-related failures in audit logs without logging credentials. 6. **Harden network deployment** - Keep the ...[truncated 193 chars]
