UNPKG

@logtape/redaction

Version:

Redact sensitive data from log messages

101 lines (99 loc) 3.19 kB
//#region field.ts /** * Default field patterns for redaction. These patterns will match * common sensitive fields such as passwords, tokens, and personal * information. * @since 0.10.0 */ const DEFAULT_REDACT_FIELDS = [ /pass(?:code|phrase|word)/i, /secret/i, /token/i, /key/i, /credential/i, /auth/i, /signature/i, /sensitive/i, /private/i, /ssn/i, /email/i, /phone/i, /address/i ]; /** * Redacts properties in a {@link LogRecord} based on the provided field * patterns and action. * * Note that it is a decorator which wraps the sink and redacts properties * before passing them to the sink. * * @example * ```ts * import { getConsoleSink } from "@logtape/logtape"; * import { redactByField } from "@logtape/redaction"; * * const sink = redactByField(getConsoleSink()); * ``` * * @param sink The sink to wrap. * @param options The redaction options. * @returns The wrapped sink. * @since 0.10.0 */ function redactByField(sink, options = DEFAULT_REDACT_FIELDS) { const opts = Array.isArray(options) ? { fieldPatterns: options } : options; const wrapped = (record) => { sink({ ...record, properties: redactProperties(record.properties, opts) }); }; if (Symbol.dispose in sink) wrapped[Symbol.dispose] = sink[Symbol.dispose]; if (Symbol.asyncDispose in sink) wrapped[Symbol.asyncDispose] = sink[Symbol.asyncDispose]; return wrapped; } /** * Redacts properties from an object based on specified field patterns. * * This function creates a shallow copy of the input object and applies * redaction rules to its properties. For properties that match the redaction * patterns, the function either removes them or transforms their values based * on the provided action. * * The redaction process is recursive and will be applied to nested objects * as well, allowing for deep redaction of sensitive data in complex object * structures. * @param properties The properties to redact. * @param options The redaction options. * @returns The redacted properties. * @since 0.10.0 */ function redactProperties(properties, options) { const copy = { ...properties }; for (const field in copy) { if (shouldFieldRedacted(field, options.fieldPatterns)) { if (options.action == null || options.action === "delete") delete copy[field]; else copy[field] = options.action(copy[field]); continue; } const value = copy[field]; if (typeof value === "object" && value !== null && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) copy[field] = redactProperties(value, options); } return copy; } /** * Checks if a field should be redacted based on the provided field patterns. * @param field The field name to check. * @param fieldPatterns The field patterns to match against. * @returns `true` if the field should be redacted, `false` otherwise. * @since 0.10.0 */ function shouldFieldRedacted(field, fieldPatterns) { for (const fieldPattern of fieldPatterns) if (typeof fieldPattern === "string") { if (fieldPattern === field) return true; } else if (fieldPattern.test(field)) return true; return false; } //#endregion exports.DEFAULT_REDACT_FIELDS = DEFAULT_REDACT_FIELDS; exports.redactByField = redactByField;