@logtape/redaction
Version:
Redact sensitive data from log messages
100 lines (99 loc) • 3.18 kB
JavaScript
//#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
export { DEFAULT_REDACT_FIELDS, redactByField };
//# sourceMappingURL=field.js.map