{"version":3,"sources":["../src/attribute-redacting-processor.ts"],"names":[],"mappings":"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","file":"chunk-ELW34S4C.cjs","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 * 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}\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  /** 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 * Default value patterns for the 'default' preset\n */\nconst DEFAULT_VALUE_PATTERNS: ValuePatternConfig[] = [\n  { name: 'email', pattern: REDACTOR_PATTERNS.email },\n  { name: 'phone', pattern: REDACTOR_PATTERNS.phone },\n  { name: 'ssn', pattern: REDACTOR_PATTERNS.ssn },\n  { name: 'creditCard', pattern: REDACTOR_PATTERNS.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\n   * Detects: emails, phone numbers, SSNs, credit cards\n   * Redacts keys: password, secret, token, apiKey, auth, credential\n   */\n  default: {\n    keyPatterns: [REDACTOR_PATTERNS.sensitiveKey],\n    valuePatterns: DEFAULT_VALUE_PATTERNS,\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, API keys in values\n   */\n  strict: {\n    keyPatterns: [REDACTOR_PATTERNS.sensitiveKey, /bearer/i, /jwt/i],\n    valuePatterns: [\n      ...DEFAULT_VALUE_PATTERNS,\n      { name: 'bearerToken', pattern: REDACTOR_PATTERNS.bearerToken },\n      { name: 'apiKeyInValue', pattern: REDACTOR_PATTERNS.apiKeyInValue },\n      { name: 'jwt', pattern: REDACTOR_PATTERNS.jwt },\n    ],\n    replacement: '[REDACTED]',\n  },\n\n  /**\n   * PCI-DSS preset - focused on payment card industry compliance\n   * Redacts: credit card numbers, 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: [\n      { name: 'creditCard', pattern: REDACTOR_PATTERNS.creditCard },\n    ],\n    replacement: '[REDACTED]',\n  },\n};\n\n/**\n * Resolve config to a normalized form\n */\nfunction resolveConfig(\n  config: AttributeRedactorConfig | AttributeRedactorPreset,\n): AttributeRedactorConfig {\n  if (typeof config === 'string') {\n    const preset = REDACTOR_PRESETS[config];\n    if (!preset) {\n      throw new Error(\n        `Unknown attribute redactor preset: \"${config}\". ` +\n          `Available presets: ${Object.keys(REDACTOR_PRESETS).join(', ')}`,\n      );\n    }\n    return preset;\n  }\n  return config;\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 defaultReplacement = config.replacement ?? '[REDACTED]';\n\n  return (key: string, value: AttributeValue): AttributeValue => {\n    // Check if key matches any sensitive key pattern\n    for (const pattern of keyPatterns) {\n      // Reset lastIndex for global regexes\n      pattern.lastIndex = 0;\n      if (pattern.test(key)) {\n        return defaultReplacement;\n      }\n    }\n\n    // For non-string values, return as-is (can't pattern match)\n    if (typeof value !== 'string') {\n      // Handle arrays of strings\n      if (Array.isArray(value)) {\n        return value.map((item) => {\n          if (typeof item === 'string') {\n            return redactStringValue(\n              item,\n              valuePatterns,\n              defaultReplacement,\n            ) as string;\n          }\n          return item;\n        }) as AttributeValue;\n      }\n      return value;\n    }\n\n    // Apply value patterns to string values\n    return redactStringValue(value, valuePatterns, defaultReplacement);\n  };\n}\n\n/**\n * Apply value patterns to a string\n */\nfunction redactStringValue(\n  value: string,\n  patterns: ValuePatternConfig[],\n  defaultReplacement: string,\n): string {\n  let result = value;\n  for (const { pattern, replacement } of patterns) {\n    // Reset lastIndex for global regexes\n    pattern.lastIndex = 0;\n    result = result.replaceAll(pattern, replacement ?? defaultReplacement);\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"]}