import { IIntegrationProvider, FindingPayload } from '../../domain/ports/IIntegrationProvider'; const SEVERITY_COLORS: Record = { critical: '#dc2626', high: '#ea580c', medium: '#ca8a04', low: '#2563eb', }; export class SlackProvider implements IIntegrationProvider { constructor(private readonly webhookUrl: string) {} async sendFinding(finding: FindingPayload): Promise { const color = SEVERITY_COLORS[finding.severity] ?? '#6b7280'; const payload = { blocks: [ { type: 'header', text: { type: 'plain_text', text: `ABE Finding: ${finding.title}`, emoji: true }, }, { type: 'section', fields: [ { type: 'mrkdwn', text: `*Severity:*\n${finding.severity.toUpperCase()}` }, { type: 'mrkdwn', text: `*Type:*\n${finding.type}` }, ], }, { type: 'section', text: { type: 'mrkdwn', text: `*Description:*\n${finding.description}` }, }, { type: 'context', elements: [ { type: 'mrkdwn', text: `Session: ${finding.sessionId}` }, ], }, ], attachments: [{ color, fallback: `${finding.severity.toUpperCase()} finding: ${finding.description}` }], }; const res = await fetch(this.webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), signal: AbortSignal.timeout(10_000), }); if (!res.ok) { throw new Error(`Slack webhook failed: ${res.status} ${await res.text()}`); } } }