export type SensitiveClassification = 'secret' | 'pii'; export interface RedactionResult { content: string; classifications: SensitiveClassification[]; } const SECRET_PATTERNS: RegExp[] = [ /\b(?:sk|ghp|gitea)_[A-Za-z0-9_-]{8,}\b/g, /\b(?:api[_-]?key|token|password|secret)\s*[:=]\s*[^\s,;]+/gi, /\b(?:authorization\s*:\s*)?bearer\s+[A-Za-z0-9._~+/-]+=*/gi, /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g, /-----BEGIN(?: [A-Z]+)* PRIVATE KEY-----[\s\S]*?-----END(?: [A-Z]+)* PRIVATE KEY-----/g, /https?:\/\/[^\s?#]+[^\s]*[?&](?:token|key|secret|signature|sig)=[^\s&#]+/gi, ]; const PII_PATTERNS: RegExp[] = [ /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, /\b\+?\d[\d(). -]{7,}\d\b/g, ]; export function redactSensitiveContent(content: string): RedactionResult { let redacted = content; const classifications: SensitiveClassification[] = []; for (const pattern of SECRET_PATTERNS) { if (pattern.test(redacted)) { classifications.push('secret'); redacted = redacted.replace(pattern, '[REDACTED_SECRET]'); } pattern.lastIndex = 0; } for (const pattern of PII_PATTERNS) { if (pattern.test(redacted)) { classifications.push('pii'); redacted = redacted.replace(pattern, '[REDACTED_PII]'); } pattern.lastIndex = 0; } return { content: redacted, classifications: [...new Set(classifications)] }; }