import { redactTelemetryText } from './redaction'; const MAX_ERROR_SUMMARY_LENGTH = 900; const SECRET_ASSIGNMENT = /\b((?:[A-Za-z][A-Za-z0-9_-]*[_-])?(?:token|secret|password|api[_ -]?key|access[_ -]?key))\b(\s*[:=]\s*)([^\s,;]+)/gi; const AUTHORIZATION_VALUE = /\bauthorization\b(\s*[:=]\s*)(?:Bearer\s+)?[^\s,;|]+/gi; const BEARER_TOKEN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi; const SLACK_WEBHOOK = /https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/_-]+/gi; const URL_SECRET = /([?&](?:token|secret|key|signature|sig|code)=)[^&\s]+/gi; const BARE_CREDENTIAL_PATTERNS = [ /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/gi, /\bgithub_pat_[A-Za-z0-9_]{20,}\b/gi, /\b(?:xaat|xain)-[A-Za-z0-9_-]{16,}\b/gi, /\bsk-ant-(?:api|oat)[A-Za-z0-9_-]{8,}\b/gi, /\bxox(?:a|b|p|r|s)-[A-Za-z0-9-]{10,}\b/gi, ] as const; export type ScheduledJobFailure = { jobId: string; jobName: string; scheduler: string; error: unknown; runUrl?: string | null; stage?: string | null; attempt?: number | null; occurredAt?: string; test?: boolean; }; export function sanitizeScheduledJobError( error: unknown, maxLength = MAX_ERROR_SUMMARY_LENGTH, ): string { const raw = error instanceof Error ? `${error.name}: ${error.message}` : typeof error === 'string' ? error : safeJson(error); let compact = redactTelemetryText(raw) .replace(/\u001b\[[0-9;]*m/g, '') .replace(/\r/g, '') .split('\n') .map((line) => line.trim()) .filter(Boolean) .join(' | ') .replace( AUTHORIZATION_VALUE, (_match, separator) => `Authorization${separator}[REDACTED]`, ) .replace(BEARER_TOKEN, 'Bearer [REDACTED]') .replace(SLACK_WEBHOOK, '[REDACTED_SLACK_WEBHOOK]') .replace(SECRET_ASSIGNMENT, (_match, label, separator) => { return `${label}${separator}[REDACTED]`; }) .replace(URL_SECRET, '$1[REDACTED]') .replace(/\s+/g, ' ') .trim(); for (const pattern of BARE_CREDENTIAL_PATTERNS) { compact = compact.replace(pattern, '[REDACTED_CREDENTIAL]'); } if (!compact) return 'No error detail was captured.'; return compact.length <= maxLength ? compact : `${compact.slice(0, Math.max(0, maxLength - 1))}…`; } export function formatScheduledJobFailure(input: ScheduledJobFailure): string { const title = input.test ? '[TEST] Scheduled job failure alert' : 'Scheduled job failed'; const errorLog = sanitizeScheduledJobError(input.error).replace( /```/g, "'''", ); return [ title, `Job: ${input.jobName} (${input.jobId})`, `Scheduler: ${input.scheduler}`, input.stage ? `Stage: ${input.stage}` : null, input.attempt ? `Attempt: ${input.attempt}` : null, `Occurred: ${input.occurredAt ?? new Date().toISOString()}`, input.runUrl ? `Diagnostics: ${input.runUrl}` : null, 'Error/log:', '```', errorLog, '```', ] .filter((line): line is string => Boolean(line)) .join('\n'); } function safeJson(value: unknown): string { try { return JSON.stringify(value) ?? String(value); } catch { return String(value); } }