claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
208 lines • 7.33 kB
JavaScript
/**
* Secure Logger Utility
*
* Provides sanitized error logging that strips sensitive information
* before logging to prevent information disclosure.
*
* Security features:
* - Removes stack traces in production
* - Sanitizes file paths to prevent internal structure disclosure
* - Filters sensitive keys from error objects
* - Truncates long messages to prevent log injection
*
* @module @claude-flow/shared/utils/secure-logger
*/
const DEFAULT_CONFIG = {
environment: process.env.NODE_ENV || 'development',
maxMessageLength: 1000,
includeStackTrace: process.env.NODE_ENV === 'development',
sensitiveKeys: [
'password', 'passwd', 'secret', 'token', 'apikey', 'api_key',
'authorization', 'auth', 'credential', 'private', 'key',
'session', 'cookie', 'jwt', 'bearer', 'access_token', 'refresh_token',
],
pathPatterns: [
/\/home\/[^/]+/g, // Unix home directories
/\/Users\/[^/]+/g, // macOS home directories
/C:\\Users\\[^\\]+/gi, // Windows user directories
/\/var\/[^/]+/g, // Var directories
/\/tmp\/[^/]+/g, // Temp directories
],
};
// ============================================================================
// Sanitization Functions
// ============================================================================
/**
* Sanitize a string message
*/
function sanitizeMessage(message, config) {
let sanitized = message;
// Truncate long messages
if (sanitized.length > config.maxMessageLength) {
sanitized = sanitized.substring(0, config.maxMessageLength) + '... [truncated]';
}
// Sanitize paths
for (const pattern of config.pathPatterns) {
sanitized = sanitized.replace(pattern, '[PATH]');
}
// Remove potential sensitive data patterns
sanitized = sanitized.replace(/[a-zA-Z0-9+/]{40,}={0,2}/g, '[REDACTED_KEY]');
sanitized = sanitized.replace(/Bearer\s+[^\s]+/gi, 'Bearer [REDACTED]');
sanitized = sanitized.replace(/token[=:]\s*[^\s&]+/gi, 'token=[REDACTED]');
return sanitized;
}
/**
* Sanitize an error object
*/
function sanitizeError(error, config) {
if (error === null || error === undefined) {
return { message: 'Unknown error' };
}
if (typeof error === 'string') {
return { message: sanitizeMessage(error, config) };
}
if (error instanceof Error) {
const sanitized = {
name: error.name,
message: sanitizeMessage(error.message, config),
};
// Only include stack in development
if (config.includeStackTrace && error.stack) {
sanitized.stack = sanitizeMessage(error.stack, config);
}
// Include code if present (common in Node.js errors)
if ('code' in error) {
sanitized.code = error.code;
}
return sanitized;
}
if (typeof error === 'object') {
return sanitizeObject(error, config);
}
return { message: String(error) };
}
/**
* Sanitize a plain object
*/
function sanitizeObject(obj, config) {
const sanitized = {};
for (const [key, value] of Object.entries(obj)) {
const lowerKey = key.toLowerCase();
// Skip sensitive keys
if (config.sensitiveKeys.some(sk => lowerKey.includes(sk))) {
sanitized[key] = '[REDACTED]';
continue;
}
// Recursively sanitize nested objects
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
sanitized[key] = sanitizeObject(value, config);
}
else if (typeof value === 'string') {
sanitized[key] = sanitizeMessage(value, config);
}
else {
sanitized[key] = value;
}
}
return sanitized;
}
// ============================================================================
// Logger Class
// ============================================================================
export class SecureLogger {
config;
prefix;
constructor(prefix = '', config = {}) {
this.prefix = prefix;
this.config = { ...DEFAULT_CONFIG, ...config };
}
/**
* Log an info message
*/
info(message, data) {
const sanitizedMessage = sanitizeMessage(message, this.config);
const sanitizedData = data ? sanitizeObject(data, this.config) : undefined;
if (sanitizedData) {
console.info(`[INFO]${this.prefix ? ` [${this.prefix}]` : ''} ${sanitizedMessage}`, sanitizedData);
}
else {
console.info(`[INFO]${this.prefix ? ` [${this.prefix}]` : ''} ${sanitizedMessage}`);
}
}
/**
* Log a warning message
*/
warn(message, data) {
const sanitizedMessage = sanitizeMessage(message, this.config);
const sanitizedData = data ? sanitizeObject(data, this.config) : undefined;
if (sanitizedData) {
console.warn(`[WARN]${this.prefix ? ` [${this.prefix}]` : ''} ${sanitizedMessage}`, sanitizedData);
}
else {
console.warn(`[WARN]${this.prefix ? ` [${this.prefix}]` : ''} ${sanitizedMessage}`);
}
}
/**
* Log an error (sanitized for security)
*/
error(message, error) {
const sanitizedMessage = sanitizeMessage(message, this.config);
const sanitizedError = error ? sanitizeError(error, this.config) : undefined;
if (sanitizedError) {
console.error(`[ERROR]${this.prefix ? ` [${this.prefix}]` : ''} ${sanitizedMessage}`, sanitizedError);
}
else {
console.error(`[ERROR]${this.prefix ? ` [${this.prefix}]` : ''} ${sanitizedMessage}`);
}
}
/**
* Log debug message (only in development)
*/
debug(message, data) {
if (this.config.environment !== 'development') {
return;
}
const sanitizedMessage = sanitizeMessage(message, this.config);
const sanitizedData = data ? sanitizeObject(data, this.config) : undefined;
if (sanitizedData) {
console.debug(`[DEBUG]${this.prefix ? ` [${this.prefix}]` : ''} ${sanitizedMessage}`, sanitizedData);
}
else {
console.debug(`[DEBUG]${this.prefix ? ` [${this.prefix}]` : ''} ${sanitizedMessage}`);
}
}
/**
* Create a child logger with a sub-prefix
*/
child(subPrefix) {
const newPrefix = this.prefix ? `${this.prefix}:${subPrefix}` : subPrefix;
return new SecureLogger(newPrefix, this.config);
}
}
// ============================================================================
// Factory Functions
// ============================================================================
/**
* Create a secure logger instance
*/
export function createSecureLogger(prefix, config) {
return new SecureLogger(prefix, config);
}
/**
* Default logger instance
*/
export const logger = createSecureLogger('claude-flow');
/**
* Sanitize an error for safe logging/display
*/
export function sanitizeErrorForLogging(error) {
return sanitizeError(error, DEFAULT_CONFIG);
}
/**
* Sanitize a message for safe logging/display
*/
export function sanitizeMessageForLogging(message) {
return sanitizeMessage(message, DEFAULT_CONFIG);
}
export default SecureLogger;
//# sourceMappingURL=secure-logger.js.map