Fix multiple small bugs for security and robustness

- Add input validation for hours and limit query parameters to prevent NaN and DoS attacks
- Replace || with ?? for proper null coalescing in metrics summary
- Fix IPv6 normalization to prevent empty string when IP is malformed
- Fix stream parsing to skip empty JSON strings and avoid parse errors
- Remove redundant .toString() calls on authorization header
This commit is contained in:
Claude
2025-11-23 04:24:46 +00:00
parent 94155233e5
commit 2770745618
2 changed files with 18 additions and 12 deletions

View File

@@ -13,8 +13,14 @@ const validatedTableName = ALLOWED_TABLES.includes(TABLE_NAME) ? TABLE_NAME : 'l
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const hours = parseInt(searchParams.get('hours') || '24', 10);
const limit = parseInt(searchParams.get('limit') || '100', 10); // Validate and sanitize hours parameter
const hoursParam = parseInt(searchParams.get('hours') || '24', 10);
const hours = !isNaN(hoursParam) && hoursParam > 0 && hoursParam <= 720 ? hoursParam : 24;
// Validate and sanitize limit parameter
const limitParam = parseInt(searchParams.get('limit') || '100', 10);
const limit = !isNaN(limitParam) && limitParam > 0 && limitParam <= 1000 ? limitParam : 100;
try { try {
const client = await pool.connect(); const client = await pool.connect();
@@ -93,12 +99,12 @@ export async function GET(request: NextRequest) {
success: true, success: true,
data: { data: {
summary: { summary: {
totalRequests: parseInt(summary.total_requests || '0'), totalRequests: parseInt(summary.total_requests ?? '0'),
totalTokens: parseInt(summary.total_tokens_used || '0'), totalTokens: parseInt(summary.total_tokens_used ?? '0'),
totalCost: parseFloat(summary.total_cost || '0'), totalCost: parseFloat(summary.total_cost ?? '0'),
avgResponseTime: parseFloat(summary.avg_response_time || '0'), avgResponseTime: parseFloat(summary.avg_response_time ?? '0'),
uniqueModels: parseInt(summary.unique_models || '0'), uniqueModels: parseInt(summary.unique_models ?? '0'),
uniqueClients: parseInt(summary.unique_clients || '0'), uniqueClients: parseInt(summary.unique_clients ?? '0'),
}, },
recentRequests, recentRequests,
modelBreakdown, modelBreakdown,

View File

@@ -33,7 +33,7 @@ function generateRequestId(): string {
function normalizeIp(ip: string | null | undefined): string | null { function normalizeIp(ip: string | null | undefined): string | null {
if (!ip) return null; if (!ip) return null;
// Handle IPv6-mapped IPv4 addresses (::ffff:x.x.x.x) // Handle IPv6-mapped IPv4 addresses (::ffff:x.x.x.x)
if (ip.startsWith('::ffff:')) { if (ip.startsWith('::ffff:') && ip.length > 7) {
return ip.substring(7); return ip.substring(7);
} }
return ip; return ip;
@@ -92,7 +92,7 @@ const server = http.createServer(async (req, res) => {
} }
const auth = req.headers["authorization"]; const auth = req.headers["authorization"];
if (!auth?.toString().startsWith("Bearer ")) { if (!auth?.startsWith("Bearer ")) {
res.statusCode = 401; res.statusCode = 401;
res.end(JSON.stringify({ error: "Missing or invalid Authorization header" })); res.end(JSON.stringify({ error: "Missing or invalid Authorization header" }));
return; return;
@@ -109,7 +109,7 @@ const server = http.createServer(async (req, res) => {
method, method,
headers: { headers: {
"Content-Type": (req.headers["content-type"] as string) || "application/json", "Content-Type": (req.headers["content-type"] as string) || "application/json",
Authorization: auth.toString(), Authorization: auth,
}, },
// @ts-ignore // @ts-ignore
duplex: "half", duplex: "half",
@@ -156,7 +156,7 @@ const server = http.createServer(async (req, res) => {
for (const line of lines) { for (const line of lines) {
if (!line.startsWith("data:")) continue; if (!line.startsWith("data:")) continue;
const jsonStr = line.slice(5).trim(); const jsonStr = line.slice(5).trim();
if (jsonStr === "[DONE]") continue; if (jsonStr === "[DONE]" || jsonStr === "") continue;
try { try {
const obj = JSON.parse(jsonStr); const obj = JSON.parse(jsonStr);
if (obj.usage) usageFromStream = obj.usage; if (obj.usage) usageFromStream = obj.usage;