54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
import { IIntegrationProvider, FindingPayload } from '../../domain/ports/IIntegrationProvider';
|
|
|
|
const SEVERITY_COLORS: Record<string, string> = {
|
|
critical: '#dc2626',
|
|
high: '#ea580c',
|
|
medium: '#ca8a04',
|
|
low: '#2563eb',
|
|
};
|
|
|
|
export class SlackProvider implements IIntegrationProvider {
|
|
constructor(private readonly webhookUrl: string) {}
|
|
|
|
async sendFinding(finding: FindingPayload): Promise<void> {
|
|
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()}`);
|
|
}
|
|
}
|
|
}
|