T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- src/index.ts:59
- Finding
- Unauthenticated HTTP Proxy Binds to All Network Interfaces<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:59-65`; `src/server/http-proxy.ts:29-79`; `src/server/http-handlers.ts:102-146` **Vulnerability Type**: Missing authentication and unsafe network binding **Risk Level**: High ### Vulnerable Code ```ts // src/index.ts:59-65 const httpEnabled = config.http.enabled || flags.http || flags['http-only']; if (httpEnabled) { const { createHttpProxy } = await import('./server/http-proxy.js'); const httpServer = createHttpProxy({ config, registry, weights, logWriter, logReader, routingTable }); httpServer.listen(config.http.port, () => { log.info(`HTTP proxy listening on http://localhost:${config.http.port}`); }); } ``` ```ts // src/server/http-proxy.ts:29-39 const server = http.createServer(async (req, res) => { // CORS headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader( 'Access-Control-Allow-Headers', 'Content-Type, Authorization, x-api-key, X-Throttle-Force-Model', ); res.setHeader( 'Access-Control-Expose-Headers', 'X-Throttle-Model, X-Throttle-Tier, X-Throttle-Score, X-Throttle-Request-Id', ); ``` ```ts // src/server/http-proxy.ts:61-79 if (req.method === 'POST') { if (pathname !== '/v1/messages' && pathname !== '/v1/chat/completions') { sendError(res, 404, 'not_found', `Unknown route: POST ${pathname}`); return; } // Parse request body const body = await readBody(req); if (pathname === '/v1/messages') { await handleMessages(body, req, res, handlerDeps); } else { await handleChatCompletions(body, req, res, handlerDeps); } return; } ``` ### Technical Analysis Calling `httpServer.listen(config.http.port)` without specifying a hostname normally binds the server to the unspecified address, potentially exposing it on all available IPv4 or IPv6 interfaces. The informational log incorrectly describes the listener as `localhost`, which can ...[truncated 2059 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Bind to loopback explicitly by default: ```ts httpServer.listen(config.http.port, '127.0.0.1', () => { log.info(`HTTP proxy listening on http://127.0.0.1:${config.http.port}`); }); ``` 2. Provide a separate, explicit configuration option for external binding and display a prominent security warning when it is enabled. 3. Require a dedicated proxy authentication token on all non-health endpoints. Compare tokens with a timing-safe comparison. 4. Do not treat incoming provider-style `Authorization` headers as authentication unless they are explicitly validated. 5. Replace wildcard CORS with a configurable allowlist. Disable browser CORS access by default. 6. Apply per-client rate limits, request quotas, concurrency limits, and conservative maximum-token limits. 7. Restrict model-forcing headers to authenticated and authorized clients. 8. Consider disabling `/stats` remotely or protecting it with the same authentication mechanism. 9. Add tests confirming that unauthenticated requests are rejected and that the default listener is loopback-only. ]]>
