import { createHash } from 'crypto'; export type AuditClassificationType = 'sms' | 'call'; export interface AuditClassificationEntry { id: string; timestamp: string; type: AuditClassificationType; phoneNumberHash: string; decision: 'spam' | 'ham' | 'block' | 'flag' | 'allow'; confidence: number; reasons: string[]; featureFlags: Record; metadata?: Record; } const MAX_AUDIT_LOG_SIZE = 10_000; class AuditLogger { private entries: AuditClassificationEntry[] = []; logClassification(entry: Omit): AuditClassificationEntry { const record: AuditClassificationEntry = { id: `audit-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, timestamp: new Date().toISOString(), ...entry, }; this.entries.push(record); if (this.entries.length > MAX_AUDIT_LOG_SIZE) { this.entries.shift(); } console.log( `[SpamShield:Audit] type=${record.type} decision=${record.decision} ` + `confidence=${record.confidence.toFixed(3)} reasons=${record.reasons.join(',') || 'none'} ` + `phoneHash=${record.phoneNumberHash}` ); return record; } getEntries( filters?: { type?: AuditClassificationType; decision?: string; startDate?: Date; endDate?: Date; limit?: number; } ): AuditClassificationEntry[] { let results = this.entries; if (filters?.type) { results = results.filter(e => e.type === filters.type); } if (filters?.decision) { results = results.filter(e => e.decision === filters.decision); } if (filters?.startDate) { results = results.filter(e => new Date(e.timestamp) >= filters.startDate!); } if (filters?.endDate) { results = results.filter(e => new Date(e.timestamp) <= filters.endDate!); } if (filters?.limit) { results = results.slice(-filters.limit); } return results; } getSummary(): { totalEntries: number; spamCount: number; hamCount: number; blockCount: number; flagCount: number; allowCount: number; avgConfidence: number; } { const spamCount = this.entries.filter(e => e.decision === 'spam' || e.decision === 'block').length; const hamCount = this.entries.filter(e => e.decision === 'ham' || e.decision === 'allow').length; const blockCount = this.entries.filter(e => e.decision === 'block').length; const flagCount = this.entries.filter(e => e.decision === 'flag').length; const allowCount = this.entries.filter(e => e.decision === 'allow').length; const avgConfidence = this.entries.length > 0 ? this.entries.reduce((s, e) => s + e.confidence, 0) / this.entries.length : 0; return { totalEntries: this.entries.length, spamCount, hamCount, blockCount, flagCount, allowCount, avgConfidence: Math.round(avgConfidence * 1000) / 1000, }; } clear(): void { this.entries = []; } } export const spamAuditLogger = new AuditLogger(); export function hashPhoneNumber(phoneNumber: string): string { const hash = createHash('sha256').update(phoneNumber.trim()).digest('hex'); return `sha256_${hash}`; }