UNPKG

autotel

Version:
1 lines 14.9 kB
{"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"]}