enum Types { Undefined = 'undefined', Null = 'null', Boolean = 'boolean', String = 'string', Number = 'number', Array = 'array', Object = 'object', Function = 'function', Error = 'error', } const MAX_CLONE_LEVEL = parseInt( process.env.TELEMETRY_LOGGER_MAX_CLONE_LEVEL ?? '5', 10, ); const SKIPPED_KEYS = [ 'request', ...(process.env.TELEMETRY_LOGGER_SKIPPED_KEYS ?? '') .trim() .split(',') .filter((v) => !!v) .map((str) => str.trim()), ]; const SKIPPED_KEY_REPLACEMENT = process.env.TELEMETRY_LOGGER_SKIPPED_REPLACEMENT ?? '>> skipped <<'; const SENSITIVE_KEYS = [ /cookie/i, /passw(or)?d/i, /^pw$/, /^pass$/i, /secret/i, /token/i, /api[-._]?key/i, /token/i, ...(process.env.TELEMETRY_LOGGER_SANITIZE ?? '') .trim() .split('||') .filter((v) => !!v) .map((str) => new RegExp(str.trim(), 'i')), ]; const SENSITIVE_KEY_REPLACEMENT = process.env.TELEMETRY_LOGGER_SENSITIVE_KEY_REPLACEMENT ?? '>> sensitive <<'; function getType(obj: unknown): Types { if (obj === null) { return Types.Null; } if (Array.isArray(obj)) { return Types.Array; } if (obj instanceof Error) { return Types.Error; } return typeof obj as Types; } function isJSONStringified(str: string): boolean { return typeof str === 'string' && ['[', '{'].includes(str[0]); } function isSkippedKey(keyStr: string): boolean { return SKIPPED_KEYS.includes(keyStr); } function isSensitiveKey(keyStr?: string): boolean { if (typeof keyStr !== 'string') { return false; } return SENSITIVE_KEYS.some((regex: RegExp) => regex.test(keyStr)); } export function clone(obj: any, level = 0): any { if (level >= MAX_CLONE_LEVEL) { return '...'; } const type = getType(obj); if (Types.Function === type) { return undefined; } if ( [ Types.Undefined, Types.Null, Types.Boolean, Types.String, Types.Number, ].includes(type) ) { return obj; } if (type === Types.Array) { return (obj as any[]).map((item) => clone(item, level + 1)); } return Object.getOwnPropertyNames(obj).reduce( (_obj: Record, key: string) => { if (isSensitiveKey(key) === true) { _obj[key] = SENSITIVE_KEY_REPLACEMENT; } else if (isSkippedKey(key) === true) { _obj[key] = SKIPPED_KEY_REPLACEMENT; } else if (isSensitiveKey(obj[key]) && isJSONStringified(obj[key])) { try { _obj[key] = JSON.stringify(clone(JSON.parse(obj[key]), level + 1)); } catch (_err) { _obj[key] = SENSITIVE_KEY_REPLACEMENT; } } else { _obj[key] = clone(obj[key], level + 1); } return _obj; }, {}, ); }