From 8b90fa2b9efe17d6594d2101fa58e9e2d63a1233 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Nov 2025 06:18:11 +0000 Subject: [PATCH] Fix critical bugs for security and data accuracy This commit addresses three important bugs: 1. SQL Injection Prevention (proxy.ts:70-75): - Added whitelist validation for DATABASE_TABLE environment variable - Table names are now validated against ALLOWED_TABLES before use - Prevents potential SQL injection through malicious table names 2. SQL Interval Parameter Bug (dashboard/app/api/metrics/route.ts): - Fixed incorrect INTERVAL syntax in PostgreSQL queries - Changed from INTERVAL '$1 hours' to INTERVAL '1 hour' * $1 - Properly uses parameterized queries with interval multiplication - Affects all 4 queries: summary, recent, model breakdown, and trends 3. Incorrect Property Reference (proxy.ts:206): - Fixed usage.cached_tokens to usage.prompt_tokens_details?.cached_tokens - Aligns with OpenAI API response structure for cached tokens - Ensures accurate logging of cached token usage --- dashboard/app/api/metrics/route.ts | 8 ++++---- proxy.ts | 9 +++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/dashboard/app/api/metrics/route.ts b/dashboard/app/api/metrics/route.ts index e65a765..3abf6d0 100644 --- a/dashboard/app/api/metrics/route.ts +++ b/dashboard/app/api/metrics/route.ts @@ -36,7 +36,7 @@ export async function GET(request: NextRequest) { COUNT(DISTINCT model) as unique_models, COUNT(DISTINCT client_ip) as unique_clients FROM ${validatedTableName} - WHERE timestamp >= NOW() - INTERVAL '$1 hours' + WHERE timestamp >= NOW() - INTERVAL '1 hour' * $1 `; const summaryResult = await client.query(summaryQuery, [hours]); const summary = summaryResult.rows[0]; @@ -56,7 +56,7 @@ export async function GET(request: NextRequest) { client_ip, stream FROM ${validatedTableName} - WHERE timestamp >= NOW() - INTERVAL '$1 hours' + WHERE timestamp >= NOW() - INTERVAL '1 hour' * $1 ORDER BY timestamp DESC LIMIT $2 `; @@ -72,7 +72,7 @@ export async function GET(request: NextRequest) { SUM(total_cost) as total_cost, AVG(response_time) as avg_response_time FROM ${validatedTableName} - WHERE timestamp >= NOW() - INTERVAL '$1 hours' + WHERE timestamp >= NOW() - INTERVAL '1 hour' * $1 GROUP BY model ORDER BY request_count DESC `; @@ -88,7 +88,7 @@ export async function GET(request: NextRequest) { SUM(total_cost) as cost, AVG(response_time) as avg_response_time FROM ${validatedTableName} - WHERE timestamp >= NOW() - INTERVAL '$1 hours' + WHERE timestamp >= NOW() - INTERVAL '1 hour' * $1 GROUP BY hour ORDER BY hour ASC `; diff --git a/proxy.ts b/proxy.ts index f3d0bfb..51e4049 100644 --- a/proxy.ts +++ b/proxy.ts @@ -67,7 +67,12 @@ async function logToPG(data: Record) { const vals = keys.map((_, i) => `$${i + 1}`).join(","); const values = Object.values(data); - await pool.query(`INSERT INTO ${process.env.DATABASE_TABLE || "llm_proxy"} (${cols}) VALUES (${vals})`, values); + // Validate table name against whitelist to prevent SQL injection + const TABLE_NAME = process.env.DATABASE_TABLE || "llm_proxy"; + const ALLOWED_TABLES = ["llm_proxy", "llm_proxy_dev", "llm_proxy_test"]; + const validatedTableName = ALLOWED_TABLES.includes(TABLE_NAME) ? TABLE_NAME : "llm_proxy"; + + await pool.query(`INSERT INTO ${validatedTableName} (${cols}) VALUES (${vals})`, values); } // --- Main proxy server --- @@ -198,7 +203,7 @@ const server = http.createServer(async (req, res) => { completion_tokens: usage.completion_tokens || null, prompt_tokens: usage.prompt_tokens || null, total_tokens: usage.total_tokens || null, - cached_tokens: usage.cached_tokens || null, + cached_tokens: usage.prompt_tokens_details?.cached_tokens || null, total_cost: totalCost, response_time: Date.now() - start, request_body: requestJson,