Spaces:
Runtime error
Runtime error
File size: 3,950 Bytes
a0de322 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
import { NextRequest, NextResponse } from 'next/server';
import { sessionManager, ErrorData } from '@/lib/session-management';
/**
* Error Analytics API Endpoints
*
* POST /api/analytics/error - Track error events
*
* Security Features:
* - Input validation and sanitization
* - Error severity classification
* - Stack trace sanitization
* - Error handling without exposing sensitive information
*/
export async function POST(request: NextRequest) {
try {
const { sessionId, error, responseTime } = await request.json();
// Input validation
if (!sessionId || typeof sessionId !== 'string') {
return NextResponse.json(
{ error: 'Valid session ID is required' },
{ status: 400 }
);
}
if (!error || typeof error !== 'object') {
return NextResponse.json(
{ error: 'Valid error object is required' },
{ status: 400 }
);
}
if (!error.type || typeof error.type !== 'string') {
return NextResponse.json(
{ error: 'Valid error type is required' },
{ status: 400 }
);
}
if (!error.message || typeof error.message !== 'string') {
return NextResponse.json(
{ error: 'Valid error message is required' },
{ status: 400 }
);
}
if (!error.severity || !['low', 'medium', 'high', 'critical'].includes(error.severity)) {
return NextResponse.json(
{ error: 'Valid error severity is required' },
{ status: 400 }
);
}
if (typeof responseTime !== 'number' || responseTime < 0) {
return NextResponse.json(
{ error: 'Valid response time is required' },
{ status: 400 }
);
}
// Sanitize error data
const sanitizedError = {
type: error.type.replace(/[<>]/g, '').substring(0, 100),
message: error.message.replace(/[<>]/g, '').substring(0, 1000),
stack: error.stack ? sanitizeStackTrace(error.stack) : undefined,
severity: error.severity
};
const errorData: ErrorData = {
eventType: 'error',
sessionId,
timestamp: new Date().toISOString(),
error: sanitizedError,
metrics: {
responseTime,
userId: null // Will be set by session manager if available
}
};
// Track error
await sessionManager.trackError(errorData);
// Check for critical errors that need immediate attention
if (error.severity === 'critical') {
console.error('CRITICAL ERROR DETECTED:', {
sessionId,
errorType: error.type,
message: error.message,
timestamp: new Date().toISOString()
});
}
return NextResponse.json({
success: true,
message: 'Error tracked successfully',
severity: error.severity
});
} catch (error) {
console.error('Error tracking error:', error);
return NextResponse.json(
{ error: 'Failed to track error' },
{ status: 500 }
);
}
}
/**
* Sanitize stack trace to remove sensitive information
* @param stackTrace - Raw stack trace string
* @returns Sanitized stack trace string
*/
function sanitizeStackTrace(stackTrace: string): string {
// Remove file paths that might contain sensitive information
let sanitized = stackTrace
.replace(/at\s+.*?\(.*?\)/g, (match) => {
// Keep function names but remove file paths
const functionMatch = match.match(/at\s+(.+?)\s+\(/);
if (functionMatch) {
return `at ${functionMatch[1]} (<sanitized>)`;
}
return match;
})
.replace(/at\s+.*?:\d+:\d+/g, (match) => {
// Remove line numbers and file paths
const functionMatch = match.match(/at\s+(.+?)\s+/);
if (functionMatch) {
return `at ${functionMatch[1]} (<sanitized>)`;
}
return match;
});
// Limit stack trace length
if (sanitized.length > 2000) {
sanitized = sanitized.substring(0, 2000) + '... (truncated)';
}
return sanitized;
} |