"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.headerRedactionService = exports.HeaderRedactionService = void 0; const logger_1 = require("../lib/logger"); const SENSITIVE_HEADERS = new Set([ 'authorization', 'x-api-key', 'x-auth-token', 'x-access-token', 'bearer', 'token', 'cookie', 'set-cookie', 'session', 'session-id', 'sessionid', 'jsessionid', 'phpsessid', 'x-real-ip', 'x-forwarded-for', 'x-client-ip', 'x-remote-addr', 'x-user-email', 'x-user-id', 'x-username', 'x-csrf-token', 'x-xsrf-token', 'x-csrftoken', 'csrf-token', 'xsrf-token', 'x-api-secret', 'x-private-key', 'x-secret', 'x-password', 'x-token', 'x-auth', 'x-authentication', 'x-forwarded-proto', 'x-forwarded-host', 'x-forwarded-server', 'x-original-forwarded-for', 'x-cluster-client-ip', 'cf-connecting-ip', 'true-client-ip', 'stripe-signature', 'paypal-auth-version', 'x-hub-signature', 'x-github-event', 'x-slack-signature', ]); const PARTIALLY_REDACTABLE_HEADERS = new Set([ 'user-agent', 'referer', 'origin', 'x-forwarded-by', 'via', ]); const SAFE_HEADERS = new Set([ 'accept', 'accept-encoding', 'accept-language', 'cache-control', 'connection', 'content-type', 'content-length', 'content-encoding', 'date', 'etag', 'expires', 'host', 'last-modified', 'location', 'pragma', 'server', 'vary', 'www-authenticate', 'x-powered-by', 'x-frame-options', 'x-content-type-options', 'strict-transport-security', 'content-security-policy', 'x-ratelimit-limit', 'x-ratelimit-remaining', 'x-ratelimit-reset', 'retry-after', 'age', 'allow', 'access-control-allow-origin', 'access-control-allow-methods', 'access-control-allow-headers', 'access-control-expose-headers', 'access-control-max-age', 'access-control-allow-credentials', ]); class HeaderRedactionService { redactHeaders(headers, options = {}) { const { includeDebugHeaders = false, redactionLevel = 'full', customSensitiveHeaders = [], preserveHeaders = [], } = options; const result = { headers: {}, redactedCount: 0, redactedHeaders: [], partiallyRedactedHeaders: [], }; const allSensitiveHeaders = new Set([ ...SENSITIVE_HEADERS, ...customSensitiveHeaders.map(h => h.toLowerCase()), ]); const preserveSet = new Set(preserveHeaders.map(h => h.toLowerCase())); for (const [key, value] of Object.entries(headers)) { const lowerKey = key.toLowerCase(); const stringValue = Array.isArray(value) ? value.join(', ') : value; if (!stringValue || stringValue.trim() === '') { continue; } if (preserveSet.has(lowerKey)) { result.headers[key] = stringValue; continue; } if (allSensitiveHeaders.has(lowerKey)) { result.redactedCount++; result.redactedHeaders.push(key); if (redactionLevel === 'partial' || includeDebugHeaders) { result.headers[key] = this.partiallyRedactValue(stringValue); } continue; } if (PARTIALLY_REDACTABLE_HEADERS.has(lowerKey)) { result.headers[key] = this.partiallyRedactValue(stringValue); result.partiallyRedactedHeaders.push(key); continue; } if (SAFE_HEADERS.has(lowerKey)) { result.headers[key] = stringValue; continue; } if (includeDebugHeaders) { result.headers[key] = this.partiallyRedactValue(stringValue); result.partiallyRedactedHeaders.push(key); } else { result.redactedCount++; result.redactedHeaders.push(key); } } logger_1.logger.debug('Headers redacted', { originalCount: Object.keys(headers).length, finalCount: Object.keys(result.headers).length, redactedCount: result.redactedCount, redactionLevel, includeDebugHeaders, }); return result; } partiallyRedactValue(value) { if (value.length <= 8) { return '*'.repeat(value.length); } const start = value.substring(0, 3); const end = value.substring(value.length - 3); const middle = '*'.repeat(Math.min(value.length - 6, 10)); return `${start}${middle}${end}`; } redactLogData(data, options = {}) { if (!data || typeof data !== 'object') { return data; } const redacted = { ...data }; if (redacted.headers) { const result = this.redactHeaders(redacted.headers, options); redacted.headers = result.headers; } if (redacted.request && redacted.request.headers) { const result = this.redactHeaders(redacted.request.headers, options); redacted.request.headers = result.headers; } if (redacted.response && redacted.response.headers) { const result = this.redactHeaders(redacted.response.headers, options); redacted.response.headers = result.headers; } if (redacted.body || redacted.data) { const bodyData = redacted.body || redacted.data; if (typeof bodyData === 'object') { redacted.body = this.redactObjectData(bodyData); redacted.data = this.redactObjectData(bodyData); } } if (redacted.url && typeof redacted.url === 'string') { redacted.url = this.redactSensitiveUrlParams(redacted.url); } if (redacted.originalUrl && typeof redacted.originalUrl === 'string') { redacted.originalUrl = this.redactSensitiveUrlParams(redacted.originalUrl); } return redacted; } redactObjectData(obj) { if (!obj || typeof obj !== 'object') { return obj; } const sensitiveFields = new Set([ 'password', 'secret', 'token', 'key', 'authorization', 'auth', 'apikey', 'api_key', 'access_token', 'refresh_token', 'session', 'cookie', 'csrf', 'xsrf', ]); const redacted = Array.isArray(obj) ? [] : {}; for (const [key, value] of Object.entries(obj)) { const lowerKey = key.toLowerCase(); if (sensitiveFields.has(lowerKey) || lowerKey.includes('password') || lowerKey.includes('secret')) { redacted[key] = typeof value === 'string' ? this.partiallyRedactValue(value) : '[REDACTED]'; } else if (typeof value === 'object' && value !== null) { redacted[key] = this.redactObjectData(value); } else { redacted[key] = value; } } return redacted; } redactSensitiveUrlParams(url) { try { const urlObj = new URL(url); const sensitiveParams = new Set([ 'token', 'key', 'secret', 'password', 'auth', 'authorization', 'api_key', 'apikey', 'access_token', 'session', 'csrf', 'xsrf', ]); for (const [key, value] of urlObj.searchParams.entries()) { if (sensitiveParams.has(key.toLowerCase()) || key.toLowerCase().includes('password') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')) { urlObj.searchParams.set(key, this.partiallyRedactValue(value)); } } return urlObj.toString(); } catch (error) { logger_1.logger.warn('Failed to parse URL for redaction:', { url, error: error.message }); return url; } } isSensitiveHeader(headerName, customSensitive = []) { const lowerName = headerName.toLowerCase(); return SENSITIVE_HEADERS.has(lowerName) || customSensitive.map(h => h.toLowerCase()).includes(lowerName); } getSensitiveHeadersList() { return Array.from(SENSITIVE_HEADERS).sort(); } validateRedactionConfig(options) { const errors = []; if (options.redactionLevel && !['full', 'partial'].includes(options.redactionLevel)) { errors.push('redactionLevel must be either "full" or "partial"'); } if (options.customSensitiveHeaders) { if (!Array.isArray(options.customSensitiveHeaders)) { errors.push('customSensitiveHeaders must be an array'); } else { for (const header of options.customSensitiveHeaders) { if (typeof header !== 'string') { errors.push('All customSensitiveHeaders must be strings'); break; } } } } if (options.preserveHeaders) { if (!Array.isArray(options.preserveHeaders)) { errors.push('preserveHeaders must be an array'); } else { for (const header of options.preserveHeaders) { if (typeof header !== 'string') { errors.push('All preserveHeaders must be strings'); break; } } } } return { valid: errors.length === 0, errors, }; } } exports.HeaderRedactionService = HeaderRedactionService; exports.headerRedactionService = new HeaderRedactionService(); //# sourceMappingURL=header-redaction.service.js.map