mirror of
https://github.com/agregarr/agregarr.git
synced 2026-03-30 00:02:40 +08:00
* fix(api): Sanitize error responses to prevent information disclosure Add error response sanitization to prevent internal implementation details from leaking to clients in production: - New errorResponse.ts utility with whitelist-based message filtering - Safe message patterns allow user-friendly errors through - Sensitive patterns block stack traces, file paths, credentials, IPs - Development mode bypasses sanitization for debugging - Updated global error handler to use sanitization This prevents information disclosure while maintaining helpful error messages for common user-facing issues like "not found" or "invalid". * style: Fix prettier formatting * ci: trigger rebuild * fix(types): Add missing type parameters for Record and Set - Add <string, string> to Record type declarations for mimeTypeMap - Add <string> to Set constructor for assetPaths Fixes TypeScript errors in CI. * style: format with prettier --------- Co-authored-by: bitr8 <bitr8@users.noreply.github.com>
87 lines
2.3 KiB
TypeScript
87 lines
2.3 KiB
TypeScript
/**
|
|
* Utility for creating safe API error responses
|
|
* Prevents leaking internal implementation details in production
|
|
*/
|
|
|
|
import logger from '@server/logger';
|
|
|
|
const isDev = process.env.NODE_ENV !== 'production';
|
|
|
|
/**
|
|
* Patterns that indicate SAFE, user-friendly error messages
|
|
*/
|
|
const SAFE_MESSAGE_PATTERNS = [
|
|
/^(not found|invalid|missing|required|failed to|unable to|cannot|unauthorized|forbidden)/i,
|
|
/^(no .+ found|.+ is required|.+ not configured)/i,
|
|
/^(connection|network|timeout)/i,
|
|
];
|
|
|
|
/**
|
|
* Patterns that indicate internal/sensitive error details
|
|
*/
|
|
const SENSITIVE_PATTERNS = [
|
|
/at\s+\S+\s+\([^)]+\)/i, // Stack trace lines
|
|
/\/home\/|\/root\/|\/var\/|\/usr\/|\/mnt\/|C:\\|D:\\/i, // File paths
|
|
/ENOENT|EACCES|EPERM|ECONNREFUSED|ETIMEDOUT|ENOTFOUND/i, // System errors
|
|
/password|secret|token|apikey|api_key|authorization/i, // Credentials
|
|
/node_modules|\.ts:\d+|\.js:\d+/i, // Internal paths/source locations
|
|
/sql|query|database|table|column|constraint/i, // Database internals
|
|
/localhost|127\.0\.0\.1|192\.168\.|10\.\d+\.|172\.(1[6-9]|2\d|3[01])\./i, // Internal IPs
|
|
];
|
|
|
|
/**
|
|
* Check if an error message is safe to show to users
|
|
*/
|
|
function isSafeMessage(message: string): boolean {
|
|
if (SENSITIVE_PATTERNS.some((pattern) => pattern.test(message))) {
|
|
return false;
|
|
}
|
|
return SAFE_MESSAGE_PATTERNS.some((pattern) => pattern.test(message));
|
|
}
|
|
|
|
/**
|
|
* Sanitize an error message for client response
|
|
* In production, only shows messages that are explicitly safe
|
|
*/
|
|
export function sanitizeErrorMessage(
|
|
error: unknown,
|
|
fallbackMessage = 'An unexpected error occurred'
|
|
): string {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
|
|
if (isDev) {
|
|
return message;
|
|
}
|
|
|
|
if (isSafeMessage(message)) {
|
|
return message;
|
|
}
|
|
|
|
return fallbackMessage;
|
|
}
|
|
|
|
/**
|
|
* Create a standardized error response object
|
|
*/
|
|
export function createErrorResponse(
|
|
error: unknown,
|
|
label: string,
|
|
userMessage: string
|
|
): { error: string; message: string } {
|
|
const fullMessage = error instanceof Error ? error.message : String(error);
|
|
const stack = error instanceof Error ? error.stack : undefined;
|
|
|
|
logger.error(`${userMessage}: ${fullMessage}`, {
|
|
label,
|
|
error: fullMessage,
|
|
stack,
|
|
});
|
|
|
|
const safeMessage = sanitizeErrorMessage(error, userMessage);
|
|
|
|
return {
|
|
error: userMessage,
|
|
message: safeMessage,
|
|
};
|
|
}
|