T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- server.js:39
- Finding
- Unauthenticated Network Query Endpoint Exposes Contact Data and Paid AI Operations<![CDATA[ ## Vulnerability Details **File Location**: `server.js:39-92` **Vulnerability Type**: Missing authentication, authorization, rate limiting, and tenant isolation **Risk Level**: High ### Vulnerable Code ```js app.post('/api/query', async (req, res) => { try { const { query, sessionId } = req.body || {}; if (!query || typeof query !== 'string' || !sessionId || typeof sessionId !== 'string') { return res.status(400).json({ error: 'Both query and sessionId are required strings.', }); } const cachedContext = await getQueryContext(sessionId); if (cachedContext && cachedContext.query === query) { return res.json({ results: cachedContext.results || [], }); } const connections = await getAllConnections(); const scoredConnections = await scoreConnections(query, connections); const topResults = await Promise.all( scoredConnections.slice(0, 5).map(async (connection) => { let actions = []; try { actions = await suggestActions(connection); } catch { actions = ['Draft intro email', 'Send quick follow-up']; } return { name: connection.name, role: connection.role, company: connection.company, platforms: connection.platforms, relevanceScore: connection.relevanceScore, reason: connection.reason, suggestedActions: actions, }; }) ); await saveQueryContext(sessionId, { query, results: topResults, createdAt: new Date().toISOString(), }); return res.json({ results: topResults }); } catch (error) { console.error('Query handling failed:', error); return res.status(500).json({ error: 'Unable to process query right now. Please try again.', }); } }); ``` ### Technical Analysis The `/api/query` route performs no authentication or authorization before loading the complete Redis-backed conne ...[truncated 2421 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require authentication on `/api/query` using a validated server-side session, signed token, or equivalent identity mechanism. 2. Enforce authorization and tenant ownership before loading connections. Replace the global `getAllConnections()` call with a query scoped to the authenticated user or organization. 3. Derive cache keys from the authenticated principal on the server, for example: ```text query-context:{authenticatedUserId}:{serverGeneratedSessionId} ``` 4. Do not trust a client-provided `sessionId` as proof of identity. Generate high-entropy identifiers server-side and verify their ownership. 5. Add IP- and account-based rate limiting, concurrency limits, and daily AI cost quotas. 6. Define strict request schemas and impose reasonable maximum lengths for `query` and `sessionId`. 7. Add abuse monitoring for repeated cache misses, high OpenAI usage, and broad contact-enumeration queries. 8. Return only the minimum contact fields required by the frontend and apply authorization checks to every returned record. 9. Retain CORS as a browser defense-in-depth control, but do not treat it as authentication or authorization. ]]>
