| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- /**
- * Production-safe logging utility for GrabZilla
- *
- * Logging levels:
- * - ERROR: Critical errors that prevent functionality
- * - WARN: Non-critical issues that may affect user experience
- * - INFO: Important state changes and operations
- * - DEBUG: Detailed debugging information (disabled in production)
- *
- * Security considerations:
- * - Never logs full file paths (only filenames)
- * - Sanitizes URLs (removes query params)
- * - Redacts sensitive data (cookie files, API keys)
- * - DEBUG level completely disabled in production
- */
- const path = require('path');
- // Determine if running in production
- const isProduction = process.env.NODE_ENV === 'production' || !process.env.NODE_ENV;
- // Log levels
- const LogLevel = {
- ERROR: 0,
- WARN: 1,
- INFO: 2,
- DEBUG: 3
- };
- // Current log level (DEBUG disabled in production)
- const currentLevel = isProduction ? LogLevel.INFO : LogLevel.DEBUG;
- /**
- * Sanitize file path to show only filename
- */
- function sanitizeFilePath(filePath) {
- if (!filePath || typeof filePath !== 'string') return '[invalid-path]';
- try {
- return path.basename(filePath);
- } catch {
- return '[path-error]';
- }
- }
- /**
- * Sanitize URL to remove query parameters
- */
- function sanitizeUrl(url) {
- if (!url || typeof url !== 'string') return '[invalid-url]';
- try {
- const parsed = new URL(url);
- return `${parsed.protocol}//${parsed.hostname}${parsed.pathname}`;
- } catch {
- // Not a valid URL, might be a file path or other string
- return '[sanitized]';
- }
- }
- /**
- * Sanitize object by removing/redacting sensitive fields
- */
- function sanitizeObject(obj) {
- if (!obj || typeof obj !== 'object') return obj;
- const sanitized = Array.isArray(obj) ? [] : {};
- for (const [key, value] of Object.entries(obj)) {
- const lowerKey = key.toLowerCase();
- // Redact sensitive fields
- if (lowerKey.includes('cookie') || lowerKey.includes('auth') || lowerKey.includes('token') || lowerKey.includes('key')) {
- sanitized[key] = '[REDACTED]';
- }
- // Sanitize file paths
- else if (lowerKey.includes('path') && typeof value === 'string') {
- sanitized[key] = sanitizeFilePath(value);
- }
- // Sanitize URLs
- else if (lowerKey.includes('url') && typeof value === 'string') {
- sanitized[key] = sanitizeUrl(value);
- }
- // Recursively sanitize nested objects
- else if (value && typeof value === 'object') {
- sanitized[key] = sanitizeObject(value);
- }
- else {
- sanitized[key] = value;
- }
- }
- return sanitized;
- }
- /**
- * Format log message with timestamp and level
- */
- function formatMessage(level, ...args) {
- const timestamp = new Date().toISOString();
- const levelStr = ['ERROR', 'WARN', 'INFO', 'DEBUG'][level];
- const prefix = `[${timestamp}] [${levelStr}]`;
- // Sanitize arguments
- const sanitizedArgs = args.map(arg => {
- if (typeof arg === 'string') {
- // Check if string looks like a URL
- if (arg.startsWith('http://') || arg.startsWith('https://')) {
- return sanitizeUrl(arg);
- }
- // Check if string looks like a file path
- if (arg.includes('/') || arg.includes('\\')) {
- return sanitizeFilePath(arg);
- }
- return arg;
- }
- if (typeof arg === 'object') {
- return sanitizeObject(arg);
- }
- return arg;
- });
- return [prefix, ...sanitizedArgs];
- }
- /**
- * Log error message (always shown)
- */
- function error(...args) {
- if (currentLevel >= LogLevel.ERROR) {
- console.error(...formatMessage(LogLevel.ERROR, ...args));
- }
- }
- /**
- * Log warning message (shown in production and development)
- */
- function warn(...args) {
- if (currentLevel >= LogLevel.WARN) {
- console.warn(...formatMessage(LogLevel.WARN, ...args));
- }
- }
- /**
- * Log info message (shown in production and development)
- */
- function info(...args) {
- if (currentLevel >= LogLevel.INFO) {
- console.log(...formatMessage(LogLevel.INFO, ...args));
- }
- }
- /**
- * Log debug message (development only)
- */
- function debug(...args) {
- if (currentLevel >= LogLevel.DEBUG) {
- console.log(...formatMessage(LogLevel.DEBUG, ...args));
- }
- }
- /**
- * Legacy console.log replacement - maps to debug level
- * Use specific levels (error/warn/info/debug) instead
- */
- function log(...args) {
- debug('[LEGACY]', ...args);
- }
- module.exports = {
- error,
- warn,
- info,
- debug,
- log,
- // Utility functions for manual sanitization if needed
- sanitizeFilePath,
- sanitizeUrl,
- sanitizeObject,
- // Expose for testing
- isProduction,
- currentLevel,
- LogLevel
- };
|