Show 82 hidden lines
+}+ export class WorkflowSanitizer {- private static readonly SENSITIVE_PATTERNS = [+ private static readonly SENSITIVE_PATTERNS: PatternDefinition[] = [ // Webhook URLs (replace with placeholder but keep structure) - MUST BE FIRST- /https?:\/\/[^\s/]+\/webhook\/[^\s]+/g,- /https?:\/\/[^\s/]+\/hook\/[^\s]+/g,-- // API keys and tokens- /sk-[a-zA-Z0-9]{16,}/g, // OpenAI keys- /Bearer\s+[^\s]+/gi, // Bearer tokens- /[a-zA-Z0-9_-]{20,}/g, // Long alphanumeric strings (API keys) - reduced threshold- /token['":\s]+[^,}]+/gi, // Token fields- /apikey['":\s]+[^,}]+/gi, // API key fields- /api_key['":\s]+[^,}]+/gi,- /secret['":\s]+[^,}]+/gi,- /password['":\s]+[^,}]+/gi,- /credential['":\s]+[^,}]+/gi,-- // URLs with authentication- /https?:\/\/[^:]+:[^@]+@[^\s/]+/g, // URLs with auth- /wss?:\/\/[^:]+:[^@]+@[^\s/]+/g,+ { pattern: /https?:\/\/[^\s/]+\/webhook\/[^\s]+/g, placeholder: '[REDACTED_WEBHOOK]' },+ { pattern: /https?:\/\/[^\s/]+\/hook\/[^\s]+/g, placeholder: '[REDACTED_WEBHOOK]' },++ // URLs with authentication - MUST BE BEFORE BEARER TOKENS+ { pattern: /https?:\/\/[^:]+:[^@]+@[^\s/]+/g, placeholder: '[REDACTED_URL_WITH_AUTH]' },+ { pattern: /wss?:\/\/[^:]+:[^@]+@[^\s/]+/g, placeholder: '[REDACTED_URL_WITH_AUTH]' },+ { pattern: /(?:postgres|mysql|mongodb|redis):\/\/[^:]+:[^@]+@[^\s]+/g, placeholder: '[REDACTED_URL_WITH_AUTH]' }, // Database protocols - includes port and path++ // API keys and tokens - ORDER MATTERS!+ // More specific patterns first, then general patterns+ { pattern: /sk-[a-zA-Z0-9]{16,}/g, placeholder: '[REDACTED_APIKEY]' }, // OpenAI keys+ { pattern: /Bearer\s+[^\s]+/gi, placeholder: 'Bearer [REDACTED]', preservePrefix: true }, // Bearer tokens+ { pattern: /\b[a-zA-Z0-9_-]{32,}\b/g, placeholder: '[REDACTED_TOKEN]' }, // Long tokens (32+ chars)+ { pattern: /\b[a-zA-Z0-9_-]{20,31}\b/g, placeholder: '[REDACTED]' }, // Short tokens (20-31 chars) // Email addresses (optional - uncomment if needed)- // /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,+ // { pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, placeholder: '[REDACTED_EMAIL]' }, ]; private static readonly SENSITIVE_FIELDS = [@@ -178,19 +181,34 @@ export class WorkflowSanitizer { const sanitized: any = {}; for (const [key, value] of Object.entries(obj)) {- // Check if key is sensitive- if (this.isSensitiveField(key)) {- sanitized[key] = '[REDACTED]';- continue;- }+ // Check if field name is sensitive+ const isSensitive = this.isSensitiveField(key);+ const isUrlField = key.toLowerCase().includes('url') ||+ key.toLowerCase().includes('endpoint') ||+ key.toLowerCase().includes('webhook'); - // Recursively sanitize nested objects+ // Recursively sanitize nested objects (unless it's a sensitive non-URL field) if (typeof value === 'object' && value !== null) {- sanitized[key] = this.sanitizeObject(value);+ if (isSensitive && !isUrlField) {+ // For sensitive object fields (like 'authentication'), redact completely+ sanitized[key] = '[REDACTED]';+ } else {+ sanitized[key] = this.sanitizeObject(value);+ } } // Sanitize string values else if (typeof value === 'string') {- sanitized[key] = this.sanitizeString(value, key);+ // For sensitive fields (except URL fields), use generic redaction+ if (isSensitive && !isUrlField) {+ sanitized[key] = '[REDACTED]';+ } else {+ // For URL fields or non-sensitive fields, use pattern-specific sanitization+ sanitized[key] = this.sanitizeString(value, key);+ }+ }+ // For non-string sensitive fields, redact completely
Reworks telemetry sanitization into pattern-specific redactions that preserve URL-shaped values by design, leaving node parameter strings only partially masked rather than fully redacted.