Files
dashcaddy/dashcaddy-api/logger-utils.js
Krystie 3c5376c7b9 security: implement Phase 1-2 fixes (logger sanitization + tests)
- Add logger-utils.js for credential sanitization in logs
- Add security comments to auth-manager.js
- Create .env.example template
- Add .env to .gitignore
- Implement comprehensive logger-utils tests (16 cases)

Desloppify score: 15.4 → ~25-30 (estimated)
Security: 62.5% → ~80%
Test coverage: 0% → ~5%

Fixes: 20 security issues flagged by Desloppify
Adds: 16 test cases
Created: 3 new files, modified 2 existing files

See SECURITY-IMPROVEMENTS.md for full details.
2026-03-21 03:43:03 +01:00

129 lines
2.9 KiB
JavaScript

/**
* Logger Utilities - Sanitize sensitive data before logging
* Created: 2026-03-21
* Purpose: Prevent credential/token/password leakage in logs
*/
/**
* List of sensitive field names that should be redacted
*/
const SENSITIVE_FIELDS = [
'password',
'passwd',
'pwd',
'secret',
'token',
'apiKey',
'api_key',
'apikey',
'auth',
'authorization',
'bearer',
'credential',
'credentials',
'key',
'privateKey',
'private_key',
'accessToken',
'access_token',
'refreshToken',
'refresh_token',
'sessionId',
'session_id',
'cookie',
'cookies',
'cert',
'certificate',
'masterKey',
'master_key',
'encryptionKey',
'encryption_key'
];
/**
* Recursively sanitize an object by redacting sensitive fields
* @param {any} data - Data to sanitize
* @param {Array<string>} additionalSensitiveKeys - Additional field names to redact
* @returns {any} Sanitized copy of the data
*/
function sanitizeForLog(data, additionalSensitiveKeys = []) {
// Handle null/undefined
if (data === null || data === undefined) {
return data;
}
// Handle primitives
if (typeof data !== 'object') {
return data;
}
// Handle arrays
if (Array.isArray(data)) {
return data.map(item => sanitizeForLog(item, additionalSensitiveKeys));
}
// Handle objects
const sensitiveKeys = [...SENSITIVE_FIELDS, ...additionalSensitiveKeys];
const sanitized = {};
for (const [key, value] of Object.entries(data)) {
const lowerKey = key.toLowerCase();
const isSensitive = sensitiveKeys.some(sk => lowerKey.includes(sk.toLowerCase()));
if (isSensitive) {
// Redact sensitive fields
sanitized[key] = '[REDACTED]';
} else if (value && typeof value === 'object') {
// Recursively sanitize nested objects
sanitized[key] = sanitizeForLog(value, additionalSensitiveKeys);
} else {
sanitized[key] = value;
}
}
return sanitized;
}
/**
* Redact a credential value for logging (show first/last 4 chars only)
* @param {string} value - Credential value
* @returns {string} Partially redacted value (e.g., "abcd****xyz")
*/
function redactCredential(value) {
if (!value || typeof value !== 'string') {
return '[REDACTED]';
}
if (value.length <= 8) {
return '[REDACTED]';
}
const start = value.slice(0, 4);
const end = value.slice(-4);
const middle = '*'.repeat(Math.min(value.length - 8, 10));
return `${start}${middle}${end}`;
}
/**
* Create a safe log message object (strips sensitive data)
* @param {string} message - Log message
* @param {object} data - Data to log
* @param {Array<string>} additionalSensitiveKeys - Additional field names to redact
* @returns {object} Safe log object
*/
function safeLog(message, data = {}, additionalSensitiveKeys = []) {
return {
message,
data: sanitizeForLog(data, additionalSensitiveKeys),
timestamp: new Date().toISOString()
};
}
module.exports = {
sanitizeForLog,
redactCredential,
safeLog,
SENSITIVE_FIELDS
};