Log Scrubber Redacting Secret Values

A structured log scrubber that masks known secret keys and bearer tokens before serialization, without mutating the original object.

JavascriptSecurity Boundaries

Code

export const REDACTED = "[REDACTED]";

const DEFAULT_SECRET_KEYS = new Set([
  "password",
  "passwd",
  "secret",
  "token",
  "accessToken",
  "refreshToken",
  "apiKey",
  "authorization",
]);

const BEARER_RE = /(Bearer\s+)[A-Za-z0-9._~+/-]+=*/g;

function maskString(value) {
  return value.replace(BEARER_RE, "$1" + REDACTED);
}

export function scrub(value, options = {}) {
  const secretKeys = new Set([...DEFAULT_SECRET_KEYS, ...(options.extraKeys ?? [])]);
  const seen = new WeakMap();

  function walk(node) {
    if (node === null || typeof node !== "object") {
      return typeof node === "string" ? maskString(node) : node;
    }
    if (seen.has(node)) return seen.get(node);

    if (Array.isArray(node)) {
      const copy = [];
      seen.set(node, copy);
      for (const item of node) copy.push(walk(item));
      return copy;
    }

    const copy = {};
    seen.set(node, copy);
    for (const [key, val] of Object.entries(node)) {
      copy[key] = secretKeys.has(key) ? REDACTED : walk(val);
    }
    return copy;
  }

  return walk(value);
}