File size: 2,187 Bytes
dcedb25 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | const MAX_PROVIDER_ERROR_LENGTH = 240;
const REDACTIONS: Array<[RegExp, string]> = [
[/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, 'Bearer [redacted]'],
// Quote-tolerant so JSON bodies like {"api_key": "..."} are caught too.
[/(["']?)\b(api[_-]?key|access[_-]?token|token|secret|authorization)\b\1(\s*[:=]\s*)(["']?)[^"',\s}\]]+/gi, '$1$2$1$3$4[redacted]'],
[/\bsk-[A-Za-z0-9_-]{8,}\b/g, '[redacted-key]'],
[/\bgsk_[A-Za-z0-9_-]{8,}\b/g, '[redacted-key]'],
[/\bfreellmapi-[A-Za-z0-9_-]{8,}\b/g, '[redacted-key]'],
[/\bAIza[0-9A-Za-z_-]{20,}\b/g, '[redacted-key]'],
[/\b[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\b/g, '[redacted-token]'],
// Bare high-entropy tokens with no known prefix: any unbroken alphanumeric
// run of 32+ chars is overwhelmingly a credential, never prose.
[/\b[A-Za-z0-9]{32,}\b/g, '[redacted-token]'],
[/\bhttps?:\/\/[^\s"'<>)]*/gi, '[redacted-url]'],
];
export function sanitizeProviderErrorMessage(message: unknown): string {
let sanitized = typeof message === 'string' ? message : String(message ?? '');
sanitized = sanitized.trim();
if (!sanitized) return 'Provider error';
for (const [pattern, replacement] of REDACTIONS) {
sanitized = sanitized.replace(pattern, replacement);
}
sanitized = sanitized.replace(/\s+/g, ' ').trim();
if (sanitized.length > MAX_PROVIDER_ERROR_LENGTH) {
sanitized = `${sanitized.slice(0, MAX_PROVIDER_ERROR_LENGTH - 3).trimEnd()}...`;
}
return sanitized;
}
// Cap for the per-hop `request_attempts.error_summary` column: one short line
// per failover attempt, tighter than the parent row's error text.
const MAX_ATTEMPT_ERROR_SUMMARY_LENGTH = 200;
/**
* The short per-attempt error summary the failover ladder stores per hop:
* the same secret/URL redactions as sanitizeProviderErrorMessage, re-capped
* at 200 chars so the drill-down stays one line per attempt.
*/
export function summarizeAttemptError(message: unknown): string {
let summary = sanitizeProviderErrorMessage(message);
if (summary.length > MAX_ATTEMPT_ERROR_SUMMARY_LENGTH) {
summary = `${summary.slice(0, MAX_ATTEMPT_ERROR_SUMMARY_LENGTH - 3).trimEnd()}...`;
}
return summary;
}
|