{"version":3,"file":"attribute-redacting-processor.cjs","names":[],"sources":["../src/attribute-redacting-processor.ts"],"sourcesContent":["/**\n * Attribute Redacting Processor\n *\n * Automatically redacts PII and sensitive data from span attributes before export.\n * This is critical for compliance (GDPR, PCI-DSS, HIPAA) and data security.\n *\n * @example Basic usage with preset\n * ```typescript\n * init({\n *   service: 'my-app',\n *   attributeRedactor: 'default'\n * })\n * ```\n *\n * @example Custom patterns\n * ```typescript\n * init({\n *   service: 'my-app',\n *   attributeRedactor: {\n *     keyPatterns: [/password/i, /secret/i],\n *     valuePatterns: [\n *       { name: 'customerId', pattern: /CUST-\\d{8}/g, replacement: 'CUST-***' }\n *     ]\n *   }\n * })\n * ```\n */\n\nimport type {\n  SpanProcessor,\n  ReadableSpan,\n} from '@opentelemetry/sdk-trace-base';\nimport type { Context, AttributeValue, Attributes } from '@opentelemetry/api';\nimport type { Span } from '@opentelemetry/sdk-trace-base';\n\n/**\n * Custom redactor function type\n */\nexport type AttributeRedactorFn = (\n  key: string,\n  value: AttributeValue,\n) => AttributeValue;\n\n/**\n * Built-in redactor preset names\n */\nexport type AttributeRedactorPreset = 'default' | 'strict' | 'pci-dss';\n\n/**\n * Masker function type - receives the matched string and returns a masked version\n */\nexport type MaskFn = (match: string) => string;\n\n/**\n * Value pattern configuration\n */\nexport interface ValuePatternConfig {\n  /** Name for debugging/logging */\n  name: string;\n  /** Regex pattern to match in values */\n  pattern: RegExp;\n  /** Custom replacement (default: uses global replacement) */\n  replacement?: string;\n  /** Mask function for smart partial masking (overrides replacement) */\n  mask?: MaskFn;\n}\n\n/**\n * Built-in PII pattern names\n */\nexport type BuiltinPatternName = keyof typeof builtinPatterns;\n\n/**\n * Attribute redactor configuration\n */\nexport interface AttributeRedactorConfig {\n  /** Patterns to match against attribute keys (redacts entire value if key matches) */\n  keyPatterns?: RegExp[];\n\n  /** Patterns to match against attribute values (redacts matched portion) */\n  valuePatterns?: ValuePatternConfig[];\n\n  /** Dot-notation paths to redact (e.g. 'user.password', 'payment.card') */\n  paths?: string[];\n\n  /** Built-in PII patterns to enable. `true` enables all, `false` disables all, array selects specific ones. */\n  builtins?: boolean | BuiltinPatternName[];\n\n  /** Custom RegExp patterns for string-level redaction */\n  patterns?: RegExp[];\n\n  /** Default replacement string (default: '[REDACTED]') */\n  replacement?: string;\n\n  /** Custom redactor function for full control */\n  redactor?: AttributeRedactorFn;\n}\n\n/**\n * Processor options\n */\nexport interface AttributeRedactingProcessorOptions {\n  redactor: AttributeRedactorConfig | AttributeRedactorPreset;\n}\n\n/**\n * Built-in patterns for detecting sensitive data\n */\nexport const REDACTOR_PATTERNS = {\n  // Value patterns (match content in attribute values)\n  email: /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/gi,\n  phone: /\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b/g,\n  ssn: /\\b\\d{3}[-]?\\d{2}[-]?\\d{4}\\b/g,\n  creditCard: /\\b\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}\\b/g,\n  bearerToken: /Bearer\\s+[A-Za-z0-9._~+/=-]+/gi,\n  apiKeyInValue: /(?:api[_-]?key|apikey|api_secret)[=:][\\s\"']*[A-Za-z0-9_-]+/gi,\n  jwt: /eyJ[A-Za-z0-9_-]*\\.eyJ[A-Za-z0-9_-]*\\.[A-Za-z0-9_-]*/g,\n\n  // Key patterns (match attribute names - redacts entire value)\n  sensitiveKey:\n    /^(password|passwd|pwd|secret|token|api[_-]?key|auth|credential|private[_-]?key|authorization)$/i,\n} as const;\n\n/**\n * Built-in PII detection patterns with smart masking.\n * Each builtin preserves just enough signal for debugging while scrubbing PII.\n */\nexport const builtinPatterns = {\n  /** Credit card numbers → ****1111 (PCI DSS: last 4 allowed) */\n  creditCard: {\n    pattern: /\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\\b/g,\n    mask: (m: string) => `****${m.replace(/[\\s-]/g, '').slice(-4)}`,\n  },\n  /** Email addresses → a***@***.com */\n  email: {\n    pattern: /[\\w.+-]+@[\\w-]+\\.[\\w.]+/g,\n    mask: (m: string) => {\n      const at = m.indexOf('@');\n      if (at < 1) return '***@***';\n      const tld = m.slice(m.lastIndexOf('.'));\n      return `${m[0]}***@***${tld}`;\n    },\n  },\n  /** IPv4 addresses → ***.***.***.100 (last octet only) */\n  ipv4: {\n    pattern:\n      /\\b(?!0\\.0\\.0\\.0\\b)(?!127\\.0\\.0\\.1\\b)\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/g,\n    mask: (m: string) => `***.***.***.${m.split('.').pop()}`,\n  },\n  /**\n   * International / formatted phone numbers.\n   *\n   * Matches:\n   * - `+33 1 23 45 67 89` -> `+33******89`\n   * - `(415) 555-1234` -> `********34`\n   * - `555-123-4567` / `555.123.4567` / `5551234567` -> `********67`\n   *\n   * Bare short digit runs like `12345678` are intentionally not matched.\n   */\n  phone: {\n    pattern:\n      /(?:\\+\\d{1,3}[\\s.-]?\\(?\\d{1,4}\\)?(?:[\\s.-]?\\d{2,4}){2,4}|\\(\\d{1,4}\\)(?:[\\s.-]?\\d{2,4}){2,4}|\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b)/g,\n    mask: (m: string) => {\n      const digits = m.replace(/[^\\d]/g, '');\n      const hasPlus = m.startsWith('+');\n      if (hasPlus && digits.length > 4) {\n        const ccMatch = m.match(/^\\+\\d{1,3}/);\n        const cc = ccMatch ? ccMatch[0] : '+';\n        return `${cc}******${digits.slice(-2)}`;\n      }\n      if (digits.length > 2) {\n        return `${'*'.repeat(digits.length - 2)}${digits.slice(-2)}`;\n      }\n      return '***';\n    },\n  },\n  /** JWT tokens → eyJ***.*** */\n  jwt: {\n    pattern: /\\beyJ[\\w-]*\\.[\\w-]*\\.[\\w-]*\\b/g,\n    mask: () => 'eyJ***.***',\n  },\n  /** Bearer tokens → Bearer *** */\n  bearer: {\n    pattern: /\\bBearer\\s+[\\w\\-.~+/]{8,}=*/gi,\n    mask: () => 'Bearer ***',\n  },\n  /** IBAN → FR76****189 (country + check digits + last 3) */\n  iban: {\n    pattern:\n      /\\b[A-Z]{2}\\d{2}[\\s-]?[\\dA-Z]{4}[\\s-]?[\\dA-Z]{4}[\\s-]?[\\dA-Z]{4}[\\s-]?[\\dA-Z]{0,4}[\\s-]?[\\dA-Z]{0,4}[\\s-]?[\\dA-Z]{0,4}\\b/g,\n    mask: (m: string) => {\n      const clean = m.replace(/[\\s-]/g, '');\n      return `${clean.slice(0, 4)}****${clean.slice(-3)}`;\n    },\n  },\n} as const;\n\nfunction cloneRegex(re: RegExp): RegExp {\n  return new RegExp(re.source, re.flags);\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction toRegExp(value: unknown): RegExp | undefined {\n  if (value instanceof RegExp) return value;\n  if (typeof value === 'string') return new RegExp(value, 'g');\n  if (isPlainObject(value) && typeof value.source === 'string') {\n    const flags = typeof value.flags === 'string' ? value.flags : 'g';\n    return new RegExp(value.source, flags);\n  }\n  return undefined;\n}\n\nfunction toRegExpArray(value: unknown): RegExp[] | undefined {\n  if (!Array.isArray(value)) return undefined;\n  const out: RegExp[] = [];\n  for (const item of value) {\n    const re = toRegExp(item);\n    if (re) out.push(re);\n  }\n  return out.length > 0 ? out : [];\n}\n\nfunction builtinToValuePattern(name: BuiltinPatternName): ValuePatternConfig {\n  const b = builtinPatterns[name];\n  return { name, pattern: cloneRegex(b.pattern), mask: b.mask };\n}\n\n/**\n * Default value patterns for the 'default' preset\n */\nconst DEFAULT_VALUE_PATTERNS: ValuePatternConfig[] = [\n  builtinToValuePattern('email'),\n  builtinToValuePattern('phone'),\n  { name: 'ssn', pattern: REDACTOR_PATTERNS.ssn },\n  builtinToValuePattern('creditCard'),\n];\n\n/**\n * Built-in redactor presets\n */\nexport const REDACTOR_PRESETS: Record<\n  AttributeRedactorPreset,\n  AttributeRedactorConfig\n> = {\n  /**\n   * Default preset - covers common PII patterns with smart masking\n   * Detects: emails (a***@***.com), phone numbers, SSNs, credit cards (****1111)\n   * Redacts keys: password, secret, token, apiKey, auth, credential\n   */\n  default: {\n    keyPatterns: [REDACTOR_PATTERNS.sensitiveKey],\n    valuePatterns: DEFAULT_VALUE_PATTERNS,\n    builtins: true,\n    replacement: '[REDACTED]',\n  },\n\n  /**\n   * Strict preset - more aggressive redaction for high-security environments\n   * Includes everything in default plus: Bearer tokens, JWTs, IBAN, API keys in values\n   */\n  strict: {\n    keyPatterns: [REDACTOR_PATTERNS.sensitiveKey, /bearer/i, /jwt/i],\n    valuePatterns: [\n      ...DEFAULT_VALUE_PATTERNS,\n      builtinToValuePattern('jwt'),\n      builtinToValuePattern('bearer'),\n      builtinToValuePattern('iban'),\n      { name: 'apiKeyInValue', pattern: REDACTOR_PATTERNS.apiKeyInValue },\n    ],\n    builtins: true,\n    replacement: '[REDACTED]',\n  },\n\n  /**\n   * PCI-DSS preset - focused on payment card industry compliance\n   * Redacts: credit card numbers (****1111), CVV-like patterns, card-related keys\n   */\n  'pci-dss': {\n    keyPatterns: [/card/i, /cvv/i, /cvc/i, /pan/i, /expir/i, /ccn/i],\n    valuePatterns: [builtinToValuePattern('creditCard')],\n    builtins: ['creditCard'],\n    replacement: '[REDACTED]',\n  },\n};\n\n/**\n * Normalize redactor config that may have been deserialized from JSON/YAML.\n * Converts regex-like values back to RegExp instances.\n */\nexport function normalizeAttributeRedactorConfig(\n  raw: AttributeRedactorConfig | AttributeRedactorPreset | unknown,\n): AttributeRedactorConfig | AttributeRedactorPreset | undefined {\n  if (raw === undefined || raw === null) return undefined;\n  if (typeof raw === 'string') return raw as AttributeRedactorPreset;\n  if (!isPlainObject(raw)) return undefined;\n\n  const config: AttributeRedactorConfig = {};\n\n  if (Array.isArray(raw.paths)) {\n    config.paths = raw.paths.filter(\n      (value): value is string => typeof value === 'string',\n    );\n  }\n\n  if (typeof raw.replacement === 'string') {\n    config.replacement = raw.replacement;\n  }\n\n  if (typeof raw.builtins === 'boolean') {\n    config.builtins = raw.builtins;\n  } else if (Array.isArray(raw.builtins)) {\n    config.builtins = raw.builtins.filter(\n      (name): name is BuiltinPatternName => typeof name === 'string',\n    );\n  }\n\n  if (typeof raw.redactor === 'function') {\n    config.redactor = raw.redactor as AttributeRedactorFn;\n  }\n\n  const keyPatterns = toRegExpArray(raw.keyPatterns);\n  if (keyPatterns) config.keyPatterns = keyPatterns;\n\n  const patterns = toRegExpArray(raw.patterns);\n  if (patterns) config.patterns = patterns;\n\n  if (Array.isArray(raw.valuePatterns)) {\n    const valuePatterns: ValuePatternConfig[] = [];\n    for (const item of raw.valuePatterns) {\n      if (!isPlainObject(item) || typeof item.name !== 'string') continue;\n      const pattern = toRegExp(item.pattern);\n      if (!pattern) continue;\n      valuePatterns.push({\n        name: item.name,\n        pattern,\n        replacement:\n          typeof item.replacement === 'string' ? item.replacement : undefined,\n        mask:\n          typeof item.mask === 'function' ? (item.mask as MaskFn) : undefined,\n      });\n    }\n    config.valuePatterns = valuePatterns;\n  }\n\n  return config;\n}\n\n/**\n * Resolve config to a normalized form\n */\nfunction resolveConfig(\n  config: AttributeRedactorConfig | AttributeRedactorPreset,\n): AttributeRedactorConfig {\n  const normalized = normalizeAttributeRedactorConfig(config);\n  if (!normalized) {\n    throw new Error('Invalid attribute redactor config');\n  }\n\n  if (typeof normalized === 'string') {\n    const preset = REDACTOR_PRESETS[normalized];\n    if (!preset) {\n      throw new Error(\n        `Unknown attribute redactor preset: \"${normalized}\". ` +\n          `Available presets: ${Object.keys(REDACTOR_PRESETS).join(', ')}`,\n      );\n    }\n    return preset;\n  }\n\n  const resolvedConfig: AttributeRedactorConfig = {\n    ...normalized,\n    keyPatterns: normalized.keyPatterns\n      ? [...normalized.keyPatterns]\n      : undefined,\n    valuePatterns: normalized.valuePatterns\n      ? [...normalized.valuePatterns]\n      : undefined,\n    paths: normalized.paths ? [...normalized.paths] : undefined,\n    patterns: normalized.patterns ? [...normalized.patterns] : undefined,\n  };\n\n  // Merge built-in patterns if enabled\n  if (resolvedConfig.builtins !== false) {\n    const builtinNames = Array.isArray(resolvedConfig.builtins)\n      ? resolvedConfig.builtins\n      : (Object.keys(builtinPatterns) as BuiltinPatternName[]);\n    const builtinValuePatterns = builtinNames\n      .filter((name) => name in builtinPatterns)\n      .map(builtinToValuePattern);\n\n    resolvedConfig.valuePatterns = [\n      ...(resolvedConfig.valuePatterns ?? []),\n      ...builtinValuePatterns,\n    ];\n  }\n\n  return resolvedConfig;\n}\n\n/**\n * Create a redactor function from config\n */\nfunction createRedactorFromConfig(\n  config: AttributeRedactorConfig,\n): AttributeRedactorFn {\n  // If custom redactor provided, use it directly\n  if (config.redactor) {\n    return config.redactor;\n  }\n\n  const keyPatterns = config.keyPatterns ?? [];\n  const valuePatterns = config.valuePatterns ?? [];\n  const paths = config.paths ?? [];\n  const pathSet = new Set(paths);\n  const customPatterns = config.patterns ?? [];\n  const defaultReplacement = config.replacement ?? '[REDACTED]';\n\n  // Build masker list from valuePatterns that have mask functions\n  const maskers: [RegExp, MaskFn][] = valuePatterns\n    .filter((vp) => vp.mask)\n    .map((vp) => [cloneRegex(vp.pattern), vp.mask!]);\n\n  return (key: string, value: AttributeValue): AttributeValue => {\n    // Key-pattern and path-based redaction only applies to string values.\n    // Numbers, booleans and other non-string attributes are not credentials;\n    // replacing them with the string '[REDACTED]' silently changes their\n    // type and corrupts downstream consumers (LLM token counters etc.).\n    if (typeof value === 'string') {\n      for (const pattern of keyPatterns) {\n        pattern.lastIndex = 0;\n        if (pattern.test(key)) {\n          return defaultReplacement;\n        }\n      }\n      if (pathSet.has(key)) {\n        return defaultReplacement;\n      }\n    }\n\n    // For non-string values, return as-is\n    if (typeof value !== 'string') {\n      if (Array.isArray(value)) {\n        return value.map((item) => {\n          if (typeof item === 'string') {\n            return redactStringValue(\n              item,\n              valuePatterns,\n              maskers,\n              customPatterns,\n              defaultReplacement,\n            ) as string;\n          }\n          return item;\n        }) as AttributeValue;\n      }\n      return value;\n    }\n\n    // Three-tier strategy: path-based → masker-based → pattern-based\n    return redactStringValue(\n      value,\n      valuePatterns,\n      maskers,\n      customPatterns,\n      defaultReplacement,\n    );\n  };\n}\n\n/**\n * Apply three-tier redaction strategy to a string\n * 1. Masker-based: built-in patterns with smart partial masking\n * 2. Pattern-based: custom RegExp patterns replaced with replacement\n */\nfunction redactStringValue(\n  value: string,\n  patterns: ValuePatternConfig[],\n  maskers: [RegExp, MaskFn][],\n  customPatterns: RegExp[],\n  defaultReplacement: string,\n): string {\n  let result = value;\n\n  // Tier 1: Apply maskers (smart partial masking)\n  for (const [pattern, mask] of maskers) {\n    pattern.lastIndex = 0;\n    result = result.replace(pattern, mask);\n  }\n\n  // Tier 2: Apply value patterns without mask (full replacement)\n  for (const { pattern, replacement, mask } of patterns) {\n    if (mask) continue; // Already handled by maskers\n    pattern.lastIndex = 0;\n    result = result.replaceAll(pattern, replacement ?? defaultReplacement);\n  }\n\n  // Tier 3: Apply custom patterns\n  for (const pattern of customPatterns) {\n    pattern.lastIndex = 0;\n    result = result.replaceAll(pattern, defaultReplacement);\n  }\n\n  return result;\n}\n\n/**\n * Create a proxy wrapper around ReadableSpan with redacted attributes\n *\n * Since ReadableSpan.attributes is readonly, we use a Proxy to intercept\n * attribute access and return the redacted version.\n */\nfunction createRedactedSpan(\n  span: ReadableSpan,\n  redactor: AttributeRedactorFn,\n): ReadableSpan {\n  // Pre-compute redacted attributes (cached for efficiency)\n  const redactedAttributes: Attributes = {};\n  for (const [key, value] of Object.entries(span.attributes)) {\n    if (value !== undefined) {\n      redactedAttributes[key] = redactor(key, value);\n    }\n  }\n\n  // Return a proxy that intercepts attribute access\n  return new Proxy(span, {\n    get(target, prop) {\n      if (prop === 'attributes') {\n        return redactedAttributes;\n      }\n      // For all other properties, delegate to the original span\n      const value = Reflect.get(target, prop);\n      // Bind methods to the original target\n      if (typeof value === 'function') {\n        return value.bind(target);\n      }\n      return value;\n    },\n  });\n}\n\n/**\n * Create an attribute redactor function from a config or preset.\n *\n * This is useful when you need to apply the same redaction logic\n * outside of the span processor pipeline (e.g., for canonical log lines).\n *\n * @example\n * ```typescript\n * const redactor = createAttributeRedactor('default');\n * const redactedValue = redactor('user.password', 'secret123');\n * // redactedValue === '[REDACTED]'\n * ```\n */\nexport function createAttributeRedactor(\n  config: AttributeRedactorConfig | AttributeRedactorPreset,\n): AttributeRedactorFn {\n  return createRedactorFromConfig(resolveConfig(config));\n}\n\n/**\n * Span processor that redacts sensitive data from span attributes.\n *\n * Redaction happens in onEnd() when all attributes are finalized.\n * Uses a Proxy wrapper to intercept attribute access since ReadableSpan\n * attributes are readonly.\n *\n * Common use cases:\n * - PII compliance (GDPR, CCPA)\n * - PCI-DSS compliance for payment data\n * - Preventing secrets from leaking to observability backends\n */\nexport class AttributeRedactingProcessor implements SpanProcessor {\n  private readonly wrappedProcessor: SpanProcessor;\n  private readonly redactor: AttributeRedactorFn;\n\n  constructor(\n    wrappedProcessor: SpanProcessor,\n    options: AttributeRedactingProcessorOptions,\n  ) {\n    this.wrappedProcessor = wrappedProcessor;\n    const config = resolveConfig(options.redactor);\n    this.redactor = createRedactorFromConfig(config);\n  }\n\n  /**\n   * Pass through onStart unchanged - attributes aren't finalized yet\n   */\n  onStart(span: Span, parentContext: Context): void {\n    this.wrappedProcessor.onStart(span, parentContext);\n  }\n\n  /**\n   * Redact attributes and forward to wrapped processor\n   */\n  onEnd(span: ReadableSpan): void {\n    try {\n      const redactedSpan = createRedactedSpan(span, this.redactor);\n      this.wrappedProcessor.onEnd(redactedSpan);\n    } catch {\n      // Fail-open: if redaction fails, forward original span\n      // This ensures we don't lose telemetry due to redaction errors\n      this.wrappedProcessor.onEnd(span);\n    }\n  }\n\n  forceFlush(): Promise<void> {\n    return this.wrappedProcessor.forceFlush();\n  }\n\n  shutdown(): Promise<void> {\n    return this.wrappedProcessor.shutdown();\n  }\n}\n\n/**\n * Export createRedactedSpan for advanced users who want to use it directly\n */\nexport { createRedactedSpan };\n"],"mappings":"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"}