
- Обновлены тесты безопасности для всех ролей (SELLER, WHOLESALE, FULFILLMENT, LOGIST) - Улучшен мониторинг и аудит доступа к коммерческим данным - Добавлена интеграция с внешними системами мониторинга - Исправлены ESLint предупреждения в компонентах поставщика - Обновлены middleware для безопасности GraphQL резолверов 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
904 lines
26 KiB
TypeScript
904 lines
26 KiB
TypeScript
/**
|
||
* Интеграция с внешними системами мониторинга
|
||
*
|
||
* Обеспечивает интеграцию системы безопасности SFERA с внешними SIEM/SOC системами,
|
||
* мониторингом инфраструктуры и системами уведомлений
|
||
*/
|
||
|
||
import { EventEmitter } from 'events'
|
||
|
||
import { PrismaClient } from '@prisma/client'
|
||
|
||
import { SecurityLogger } from '../../lib/security-logger'
|
||
|
||
import { SecurityAlert } from './types'
|
||
|
||
/**
|
||
* Конфигурация интеграций
|
||
*/
|
||
interface IntegrationConfig {
|
||
siem: {
|
||
enabled: boolean
|
||
type: 'SPLUNK' | 'ELASTIC_SIEM' | 'QRADAR' | 'SENTINEL'
|
||
endpoint: string
|
||
apiKey: string
|
||
format: 'CEF' | 'JSON' | 'SYSLOG'
|
||
}
|
||
prometheus: {
|
||
enabled: boolean
|
||
pushGateway: string
|
||
jobName: string
|
||
instance: string
|
||
}
|
||
grafana: {
|
||
enabled: boolean
|
||
apiUrl: string
|
||
apiKey: string
|
||
dashboardId: string
|
||
}
|
||
datadog: {
|
||
enabled: boolean
|
||
apiKey: string
|
||
appKey: string
|
||
site: string
|
||
}
|
||
newrelic: {
|
||
enabled: boolean
|
||
licenseKey: string
|
||
appId: string
|
||
}
|
||
slack: {
|
||
enabled: boolean
|
||
webhookUrl: string
|
||
channel: string
|
||
}
|
||
teams: {
|
||
enabled: boolean
|
||
webhookUrl: string
|
||
}
|
||
pagerduty: {
|
||
enabled: boolean
|
||
integrationKey: string
|
||
severity: 'critical' | 'error' | 'warning' | 'info'
|
||
}
|
||
webhook: {
|
||
enabled: boolean
|
||
url: string
|
||
headers: Record<string, string>
|
||
retries: number
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Метрики для экспорта
|
||
*/
|
||
interface SecurityMetrics {
|
||
timestamp: Date
|
||
totalAlerts: number
|
||
alertsBySeverity: Record<string, number>
|
||
alertsByType: Record<string, number>
|
||
activeThreats: number
|
||
riskScore: number
|
||
systemHealth: {
|
||
monitoring: number
|
||
alerts: number
|
||
audit: number
|
||
filtering: number
|
||
}
|
||
userActivity: {
|
||
totalAccesses: number
|
||
uniqueUsers: number
|
||
suspiciousActivity: number
|
||
}
|
||
performance: {
|
||
avgResponseTime: number
|
||
processingRate: number
|
||
errorRate: number
|
||
}
|
||
}
|
||
|
||
/**
|
||
* События для внешних систем
|
||
*/
|
||
interface SecurityEvent {
|
||
id: string
|
||
timestamp: Date
|
||
eventType: 'ALERT_GENERATED' | 'THREAT_DETECTED' | 'SYSTEM_STATUS' | 'USER_ACTIVITY'
|
||
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
|
||
source: string
|
||
data: Record<string, unknown>
|
||
tags: string[]
|
||
}
|
||
|
||
export class ExternalMonitoringIntegration extends EventEmitter {
|
||
private static instance: ExternalMonitoringIntegration
|
||
private config: IntegrationConfig
|
||
private isActive = false
|
||
private metricsBuffer: SecurityMetrics[] = []
|
||
private eventsBuffer: SecurityEvent[] = []
|
||
|
||
constructor(private prisma: PrismaClient) {
|
||
super()
|
||
this.loadConfiguration()
|
||
}
|
||
|
||
/**
|
||
* Получить singleton instance
|
||
*/
|
||
static getInstance(prisma: PrismaClient): ExternalMonitoringIntegration {
|
||
if (!ExternalMonitoringIntegration.instance) {
|
||
ExternalMonitoringIntegration.instance = new ExternalMonitoringIntegration(prisma)
|
||
}
|
||
return ExternalMonitoringIntegration.instance
|
||
}
|
||
|
||
/**
|
||
* Запуск интеграций
|
||
*/
|
||
async start(): Promise<void> {
|
||
if (this.isActive) {
|
||
return
|
||
}
|
||
|
||
this.isActive = true
|
||
|
||
// Периодическая отправка метрик (каждые 30 секунд)
|
||
setInterval(() => {
|
||
this.pushMetrics()
|
||
}, 30000)
|
||
|
||
// Периодическая отправка событий (каждые 5 секунд)
|
||
setInterval(() => {
|
||
this.pushEvents()
|
||
}, 5000)
|
||
|
||
// Проверка состояния интеграций (каждые 5 минут)
|
||
setInterval(() => {
|
||
this.healthCheck()
|
||
}, 5 * 60 * 1000)
|
||
|
||
SecurityLogger.logSecurityInfo({
|
||
message: 'External monitoring integration started',
|
||
integrations: Object.keys(this.config).filter((key) => this.config[key as keyof IntegrationConfig]?.enabled),
|
||
})
|
||
|
||
this.emit('integration_started', {
|
||
timestamp: new Date(),
|
||
activeIntegrations: this.getActiveIntegrations(),
|
||
})
|
||
}
|
||
|
||
/**
|
||
* Остановка интеграций
|
||
*/
|
||
stop(): void {
|
||
this.isActive = false
|
||
this.removeAllListeners()
|
||
SecurityLogger.logSecurityInfo({ message: 'External monitoring integration stopped' })
|
||
}
|
||
|
||
/**
|
||
* Отправка алерта во внешние системы
|
||
*/
|
||
async sendSecurityAlert(alert: SecurityAlert): Promise<void> {
|
||
if (!this.isActive) {
|
||
return
|
||
}
|
||
|
||
const event: SecurityEvent = {
|
||
id: alert.id,
|
||
timestamp: alert.timestamp,
|
||
eventType: 'ALERT_GENERATED',
|
||
severity: alert.severity,
|
||
source: 'SFERA_SECURITY',
|
||
data: {
|
||
alertType: alert.type,
|
||
userId: alert.userId,
|
||
message: alert.message,
|
||
metadata: alert.metadata,
|
||
},
|
||
tags: ['security', 'alert', alert.type.toLowerCase(), alert.severity.toLowerCase()],
|
||
}
|
||
|
||
this.eventsBuffer.push(event)
|
||
|
||
// Для критичных алертов - немедленная отправка
|
||
if (alert.severity === 'CRITICAL' || alert.severity === 'HIGH') {
|
||
await this.pushCriticalEvent(event)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Отправка метрик безопасности
|
||
*/
|
||
async sendSecurityMetrics(metrics: SecurityMetrics): Promise<void> {
|
||
if (!this.isActive) {
|
||
return
|
||
}
|
||
|
||
this.metricsBuffer.push(metrics)
|
||
|
||
// Если буфер переполнен - принудительная отправка
|
||
if (this.metricsBuffer.length > 100) {
|
||
await this.pushMetrics()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Отправка события обнаружения угрозы
|
||
*/
|
||
async sendThreatDetection(threat: {
|
||
id: string
|
||
modelId: string
|
||
userId: string
|
||
threatType: string
|
||
confidence: number
|
||
riskScore: number
|
||
indicators: string[]
|
||
evidence: Record<string, unknown>
|
||
}): Promise<void> {
|
||
if (!this.isActive) {
|
||
return
|
||
}
|
||
|
||
const event: SecurityEvent = {
|
||
id: threat.id,
|
||
timestamp: new Date(),
|
||
eventType: 'THREAT_DETECTED',
|
||
severity: threat.riskScore > 80 ? 'CRITICAL' : threat.riskScore > 60 ? 'HIGH' : 'MEDIUM',
|
||
source: 'SFERA_THREAT_DETECTION',
|
||
data: {
|
||
modelId: threat.modelId,
|
||
userId: threat.userId,
|
||
threatType: threat.threatType,
|
||
confidence: threat.confidence,
|
||
riskScore: threat.riskScore,
|
||
indicators: threat.indicators,
|
||
evidence: threat.evidence,
|
||
},
|
||
tags: ['security', 'threat', 'detection', threat.threatType.toLowerCase()],
|
||
}
|
||
|
||
this.eventsBuffer.push(event)
|
||
|
||
// Для высокого risk score - немедленная отправка
|
||
if (threat.riskScore > 70) {
|
||
await this.pushCriticalEvent(event)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Обновление статуса системы
|
||
*/
|
||
async sendSystemStatus(status: {
|
||
monitoring: { status: string; uptime: number; metrics: Record<string, unknown> }
|
||
alerts: { status: string; uptime: number; metrics: Record<string, unknown> }
|
||
audit: { status: string; uptime: number; metrics: Record<string, unknown> }
|
||
dataFiltering: { status: string; uptime: number; metrics: Record<string, unknown> }
|
||
overallHealth: number
|
||
}): Promise<void> {
|
||
if (!this.isActive) {
|
||
return
|
||
}
|
||
|
||
const event: SecurityEvent = {
|
||
id: `system-status-${Date.now()}`,
|
||
timestamp: new Date(),
|
||
eventType: 'SYSTEM_STATUS',
|
||
severity: status.overallHealth > 95 ? 'LOW' : status.overallHealth > 80 ? 'MEDIUM' : 'HIGH',
|
||
source: 'SFERA_SYSTEM',
|
||
data: status,
|
||
tags: ['system', 'status', 'health'],
|
||
}
|
||
|
||
this.eventsBuffer.push(event)
|
||
}
|
||
|
||
/**
|
||
* Загрузка конфигурации
|
||
*/
|
||
private loadConfiguration(): void {
|
||
this.config = {
|
||
siem: {
|
||
enabled: process.env.SIEM_INTEGRATION_ENABLED === 'true',
|
||
type: (process.env.SIEM_TYPE as any) || 'ELASTIC_SIEM',
|
||
endpoint: process.env.SIEM_ENDPOINT || '',
|
||
apiKey: process.env.SIEM_API_KEY || '',
|
||
format: (process.env.SIEM_FORMAT as any) || 'JSON',
|
||
},
|
||
prometheus: {
|
||
enabled: process.env.PROMETHEUS_ENABLED === 'true',
|
||
pushGateway: process.env.PROMETHEUS_PUSH_GATEWAY || 'http://localhost:9091',
|
||
jobName: process.env.PROMETHEUS_JOB_NAME || 'sfera-security',
|
||
instance: process.env.PROMETHEUS_INSTANCE || 'sfera-api-01',
|
||
},
|
||
grafana: {
|
||
enabled: process.env.GRAFANA_ENABLED === 'true',
|
||
apiUrl: process.env.GRAFANA_API_URL || '',
|
||
apiKey: process.env.GRAFANA_API_KEY || '',
|
||
dashboardId: process.env.GRAFANA_DASHBOARD_ID || '',
|
||
},
|
||
datadog: {
|
||
enabled: process.env.DATADOG_ENABLED === 'true',
|
||
apiKey: process.env.DATADOG_API_KEY || '',
|
||
appKey: process.env.DATADOG_APP_KEY || '',
|
||
site: process.env.DATADOG_SITE || 'datadoghq.com',
|
||
},
|
||
newrelic: {
|
||
enabled: process.env.NEWRELIC_ENABLED === 'true',
|
||
licenseKey: process.env.NEWRELIC_LICENSE_KEY || '',
|
||
appId: process.env.NEWRELIC_APP_ID || '',
|
||
},
|
||
slack: {
|
||
enabled: process.env.SLACK_INTEGRATION_ENABLED === 'true',
|
||
webhookUrl: process.env.SLACK_WEBHOOK_URL || '',
|
||
channel: process.env.SLACK_CHANNEL || '#security-alerts',
|
||
},
|
||
teams: {
|
||
enabled: process.env.TEAMS_INTEGRATION_ENABLED === 'true',
|
||
webhookUrl: process.env.TEAMS_WEBHOOK_URL || '',
|
||
},
|
||
pagerduty: {
|
||
enabled: process.env.PAGERDUTY_ENABLED === 'true',
|
||
integrationKey: process.env.PAGERDUTY_INTEGRATION_KEY || '',
|
||
severity: (process.env.PAGERDUTY_SEVERITY as any) || 'error',
|
||
},
|
||
webhook: {
|
||
enabled: process.env.WEBHOOK_INTEGRATION_ENABLED === 'true',
|
||
url: process.env.WEBHOOK_URL || '',
|
||
headers: JSON.parse(process.env.WEBHOOK_HEADERS || '{}'),
|
||
retries: parseInt(process.env.WEBHOOK_RETRIES || '3'),
|
||
},
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Отправка метрик
|
||
*/
|
||
private async pushMetrics(): Promise<void> {
|
||
if (this.metricsBuffer.length === 0) {
|
||
return
|
||
}
|
||
|
||
const metrics = [...this.metricsBuffer]
|
||
this.metricsBuffer.length = 0
|
||
|
||
const promises: Promise<void>[] = []
|
||
|
||
if (this.config.prometheus.enabled) {
|
||
promises.push(this.sendToPrometheus(metrics))
|
||
}
|
||
|
||
if (this.config.datadog.enabled) {
|
||
promises.push(this.sendToDatadog(metrics))
|
||
}
|
||
|
||
if (this.config.newrelic.enabled) {
|
||
promises.push(this.sendToNewRelic(metrics))
|
||
}
|
||
|
||
if (this.config.grafana.enabled) {
|
||
promises.push(this.sendToGrafana(metrics))
|
||
}
|
||
|
||
await Promise.allSettled(promises)
|
||
}
|
||
|
||
/**
|
||
* Отправка событий
|
||
*/
|
||
private async pushEvents(): Promise<void> {
|
||
if (this.eventsBuffer.length === 0) {
|
||
return
|
||
}
|
||
|
||
const events = [...this.eventsBuffer]
|
||
this.eventsBuffer.length = 0
|
||
|
||
const promises: Promise<void>[] = []
|
||
|
||
if (this.config.siem.enabled) {
|
||
promises.push(this.sendToSIEM(events))
|
||
}
|
||
|
||
if (this.config.webhook.enabled) {
|
||
promises.push(this.sendToWebhook(events))
|
||
}
|
||
|
||
await Promise.allSettled(promises)
|
||
}
|
||
|
||
/**
|
||
* Немедленная отправка критичного события
|
||
*/
|
||
private async pushCriticalEvent(event: SecurityEvent): Promise<void> {
|
||
const promises: Promise<void>[] = []
|
||
|
||
if (this.config.slack.enabled) {
|
||
promises.push(this.sendToSlack([event]))
|
||
}
|
||
|
||
if (this.config.teams.enabled) {
|
||
promises.push(this.sendToTeams([event]))
|
||
}
|
||
|
||
if (this.config.pagerduty.enabled && event.severity === 'CRITICAL') {
|
||
promises.push(this.sendToPagerDuty([event]))
|
||
}
|
||
|
||
await Promise.allSettled(promises)
|
||
}
|
||
|
||
/**
|
||
* Отправка в SIEM систему
|
||
*/
|
||
private async sendToSIEM(events: SecurityEvent[]): Promise<void> {
|
||
try {
|
||
const payload = events.map((event) => this.formatSIEMEvent(event))
|
||
|
||
const response = await fetch(this.config.siem.endpoint, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
Authorization: `Bearer ${this.config.siem.apiKey}`,
|
||
},
|
||
body: JSON.stringify({ events: payload }),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`SIEM API error: ${response.status} ${response.statusText}`)
|
||
}
|
||
|
||
SecurityLogger.logSecurityInfo({
|
||
message: 'Events sent to SIEM',
|
||
count: events.length,
|
||
siemType: this.config.siem.type,
|
||
})
|
||
} catch (error) {
|
||
SecurityLogger.logSecurityError(error as Error, {
|
||
operation: 'sendToSIEM',
|
||
eventsCount: events.length,
|
||
})
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Отправка в Prometheus
|
||
*/
|
||
private async sendToPrometheus(metrics: SecurityMetrics[]): Promise<void> {
|
||
try {
|
||
const latestMetrics = metrics[metrics.length - 1]
|
||
|
||
const prometheusMetrics = this.formatPrometheusMetrics(latestMetrics)
|
||
|
||
const response = await fetch(`${this.config.prometheus.pushGateway}/metrics/job/${this.config.prometheus.jobName}/instance/${this.config.prometheus.instance}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'text/plain',
|
||
},
|
||
body: prometheusMetrics,
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`Prometheus API error: ${response.status} ${response.statusText}`)
|
||
}
|
||
|
||
SecurityLogger.logSecurityInfo({
|
||
message: 'Metrics sent to Prometheus',
|
||
metricsCount: Object.keys(latestMetrics).length,
|
||
})
|
||
} catch (error) {
|
||
SecurityLogger.logSecurityError(error as Error, {
|
||
operation: 'sendToPrometheus',
|
||
metricsCount: metrics.length,
|
||
})
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Отправка в Slack
|
||
*/
|
||
private async sendToSlack(events: SecurityEvent[]): Promise<void> {
|
||
try {
|
||
for (const event of events.filter(e => e.severity === 'CRITICAL' || e.severity === 'HIGH')) {
|
||
const slackPayload = {
|
||
channel: this.config.slack.channel,
|
||
username: 'SFERA Security Bot',
|
||
icon_emoji: ':warning:',
|
||
attachments: [
|
||
{
|
||
color: this.getSlackColor(event.severity),
|
||
title: `🚨 Security Alert: ${event.eventType}`,
|
||
text: `${event.data.message || 'Security event detected'}`,
|
||
fields: [
|
||
{ title: 'Severity', value: event.severity, short: true },
|
||
{ title: 'Source', value: event.source, short: true },
|
||
{ title: 'Event ID', value: event.id, short: true },
|
||
{ title: 'Timestamp', value: event.timestamp.toISOString(), short: true },
|
||
],
|
||
footer: 'SFERA Security System',
|
||
ts: Math.floor(event.timestamp.getTime() / 1000),
|
||
},
|
||
],
|
||
}
|
||
|
||
const response = await fetch(this.config.slack.webhookUrl, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(slackPayload),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`Slack API error: ${response.status} ${response.statusText}`)
|
||
}
|
||
}
|
||
|
||
SecurityLogger.logSecurityInfo({
|
||
message: 'Critical events sent to Slack',
|
||
count: events.length,
|
||
})
|
||
} catch (error) {
|
||
SecurityLogger.logSecurityError(error as Error, {
|
||
operation: 'sendToSlack',
|
||
eventsCount: events.length,
|
||
})
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Отправка в Microsoft Teams
|
||
*/
|
||
private async sendToTeams(events: SecurityEvent[]): Promise<void> {
|
||
try {
|
||
for (const event of events.filter(e => e.severity === 'CRITICAL' || e.severity === 'HIGH')) {
|
||
const teamsPayload = {
|
||
'@type': 'MessageCard',
|
||
'@context': 'http://schema.org/extensions',
|
||
themeColor: this.getTeamsColor(event.severity),
|
||
summary: `SFERA Security Alert: ${event.eventType}`,
|
||
sections: [
|
||
{
|
||
activityTitle: `🚨 Security Alert: ${event.eventType}`,
|
||
activitySubtitle: event.data.message || 'Security event detected',
|
||
facts: [
|
||
{ name: 'Severity', value: event.severity },
|
||
{ name: 'Source', value: event.source },
|
||
{ name: 'Event ID', value: event.id },
|
||
{ name: 'Timestamp', value: event.timestamp.toISOString() },
|
||
],
|
||
markdown: true,
|
||
},
|
||
],
|
||
}
|
||
|
||
const response = await fetch(this.config.teams.webhookUrl, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(teamsPayload),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`Teams API error: ${response.status} ${response.statusText}`)
|
||
}
|
||
}
|
||
|
||
SecurityLogger.logSecurityInfo({
|
||
message: 'Critical events sent to Teams',
|
||
count: events.length,
|
||
})
|
||
} catch (error) {
|
||
SecurityLogger.logSecurityError(error as Error, {
|
||
operation: 'sendToTeams',
|
||
eventsCount: events.length,
|
||
})
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Отправка в PagerDuty
|
||
*/
|
||
private async sendToPagerDuty(events: SecurityEvent[]): Promise<void> {
|
||
try {
|
||
for (const event of events.filter(e => e.severity === 'CRITICAL')) {
|
||
const pagerDutyPayload = {
|
||
routing_key: this.config.pagerduty.integrationKey,
|
||
event_action: 'trigger',
|
||
payload: {
|
||
summary: `SFERA Security Alert: ${event.eventType}`,
|
||
severity: this.config.pagerduty.severity,
|
||
source: event.source,
|
||
timestamp: event.timestamp.toISOString(),
|
||
custom_details: {
|
||
event_id: event.id,
|
||
event_type: event.eventType,
|
||
data: event.data,
|
||
tags: event.tags,
|
||
},
|
||
},
|
||
}
|
||
|
||
const response = await fetch('https://events.pagerduty.com/v2/enqueue', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(pagerDutyPayload),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`PagerDuty API error: ${response.status} ${response.statusText}`)
|
||
}
|
||
}
|
||
|
||
SecurityLogger.logSecurityInfo({
|
||
message: 'Critical events sent to PagerDuty',
|
||
count: events.length,
|
||
})
|
||
} catch (error) {
|
||
SecurityLogger.logSecurityError(error as Error, {
|
||
operation: 'sendToPagerDuty',
|
||
eventsCount: events.length,
|
||
})
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Отправка в webhook
|
||
*/
|
||
private async sendToWebhook(events: SecurityEvent[]): Promise<void> {
|
||
let attempts = 0
|
||
const maxAttempts = this.config.webhook.retries
|
||
|
||
while (attempts < maxAttempts) {
|
||
try {
|
||
const response = await fetch(this.config.webhook.url, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
...this.config.webhook.headers,
|
||
},
|
||
body: JSON.stringify({
|
||
events,
|
||
timestamp: new Date().toISOString(),
|
||
source: 'SFERA_SECURITY',
|
||
}),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`Webhook API error: ${response.status} ${response.statusText}`)
|
||
}
|
||
|
||
SecurityLogger.logSecurityInfo({
|
||
message: 'Events sent to webhook',
|
||
count: events.length,
|
||
attempts: attempts + 1,
|
||
})
|
||
|
||
return
|
||
} catch (error) {
|
||
attempts++
|
||
if (attempts >= maxAttempts) {
|
||
SecurityLogger.logSecurityError(error as Error, {
|
||
operation: 'sendToWebhook',
|
||
eventsCount: events.length,
|
||
attempts,
|
||
})
|
||
return
|
||
}
|
||
|
||
// Экспоненциальная задержка
|
||
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempts) * 1000))
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Заглушки для других интеграций
|
||
*/
|
||
private async sendToDatadog(metrics: SecurityMetrics[]): Promise<void> {
|
||
// TODO: Реализовать интеграцию с Datadog
|
||
console.log('📊 Datadog integration placeholder', metrics.length)
|
||
}
|
||
|
||
private async sendToNewRelic(metrics: SecurityMetrics[]): Promise<void> {
|
||
// TODO: Реализовать интеграцию с New Relic
|
||
console.log('📈 New Relic integration placeholder', metrics.length)
|
||
}
|
||
|
||
private async sendToGrafana(metrics: SecurityMetrics[]): Promise<void> {
|
||
// TODO: Реализовать интеграцию с Grafana
|
||
console.log('📋 Grafana integration placeholder', metrics.length)
|
||
}
|
||
|
||
/**
|
||
* Форматирование события для SIEM
|
||
*/
|
||
private formatSIEMEvent(event: SecurityEvent): Record<string, unknown> {
|
||
switch (this.config.siem.format) {
|
||
case 'CEF':
|
||
return this.formatCEF(event)
|
||
case 'SYSLOG':
|
||
return this.formatSyslog(event)
|
||
default:
|
||
return {
|
||
timestamp: event.timestamp.toISOString(),
|
||
id: event.id,
|
||
event_type: event.eventType,
|
||
severity: event.severity,
|
||
source: event.source,
|
||
data: event.data,
|
||
tags: event.tags,
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Форматирование в CEF
|
||
*/
|
||
private formatCEF(event: SecurityEvent): Record<string, unknown> {
|
||
return {
|
||
cef_version: '0',
|
||
device_vendor: 'SFERA',
|
||
device_product: 'Security System',
|
||
device_version: '3.0.0',
|
||
signature_id: event.eventType,
|
||
name: event.id,
|
||
severity: this.mapCEFSeverity(event.severity),
|
||
extension: {
|
||
rt: event.timestamp.getTime(),
|
||
src: event.source,
|
||
msg: JSON.stringify(event.data),
|
||
},
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Форматирование в Syslog
|
||
*/
|
||
private formatSyslog(event: SecurityEvent): Record<string, unknown> {
|
||
const priority = this.mapSyslogPriority(event.severity)
|
||
return {
|
||
priority,
|
||
timestamp: event.timestamp.toISOString(),
|
||
hostname: 'sfera-security',
|
||
tag: 'SFERA-SEC',
|
||
message: `[${event.eventType}] ${event.id}: ${JSON.stringify(event.data)}`,
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Форматирование метрик для Prometheus
|
||
*/
|
||
private formatPrometheusMetrics(metrics: SecurityMetrics): string {
|
||
const lines: string[] = []
|
||
const timestamp = metrics.timestamp.getTime()
|
||
|
||
lines.push('# HELP sfera_security_alerts_total Total number of security alerts')
|
||
lines.push('# TYPE sfera_security_alerts_total counter')
|
||
lines.push(`sfera_security_alerts_total ${metrics.totalAlerts} ${timestamp}`)
|
||
|
||
lines.push('# HELP sfera_security_active_threats Current number of active threats')
|
||
lines.push('# TYPE sfera_security_active_threats gauge')
|
||
lines.push(`sfera_security_active_threats ${metrics.activeThreats} ${timestamp}`)
|
||
|
||
lines.push('# HELP sfera_security_risk_score Current overall risk score')
|
||
lines.push('# TYPE sfera_security_risk_score gauge')
|
||
lines.push(`sfera_security_risk_score ${metrics.riskScore} ${timestamp}`)
|
||
|
||
lines.push('# HELP sfera_security_user_accesses_total Total number of user data accesses')
|
||
lines.push('# TYPE sfera_security_user_accesses_total counter')
|
||
lines.push(`sfera_security_user_accesses_total ${metrics.userActivity.totalAccesses} ${timestamp}`)
|
||
|
||
return lines.join('\n')
|
||
}
|
||
|
||
/**
|
||
* Получение активных интеграций
|
||
*/
|
||
private getActiveIntegrations(): string[] {
|
||
const active: string[] = []
|
||
|
||
Object.entries(this.config).forEach(([name, config]) => {
|
||
if (config.enabled) {
|
||
active.push(name)
|
||
}
|
||
})
|
||
|
||
return active
|
||
}
|
||
|
||
/**
|
||
* Проверка здоровья интеграций
|
||
*/
|
||
private async healthCheck(): Promise<void> {
|
||
const healthStatus: Record<string, boolean> = {}
|
||
|
||
// Проверяем доступность каждой интеграции
|
||
if (this.config.siem.enabled) {
|
||
healthStatus.siem = await this.checkSIEMHealth()
|
||
}
|
||
|
||
if (this.config.prometheus.enabled) {
|
||
healthStatus.prometheus = await this.checkPrometheusHealth()
|
||
}
|
||
|
||
if (this.config.slack.enabled) {
|
||
healthStatus.slack = await this.checkSlackHealth()
|
||
}
|
||
|
||
SecurityLogger.logSecurityInfo({
|
||
message: 'Integration health check completed',
|
||
status: healthStatus,
|
||
})
|
||
|
||
this.emit('health_check', {
|
||
timestamp: new Date(),
|
||
status: healthStatus,
|
||
})
|
||
}
|
||
|
||
/**
|
||
* Заглушки для проверки здоровья
|
||
*/
|
||
private async checkSIEMHealth(): Promise<boolean> {
|
||
// TODO: Реализовать проверку SIEM
|
||
return true
|
||
}
|
||
|
||
private async checkPrometheusHealth(): Promise<boolean> {
|
||
// TODO: Реализовать проверку Prometheus
|
||
return true
|
||
}
|
||
|
||
private async checkSlackHealth(): Promise<boolean> {
|
||
// TODO: Реализовать проверку Slack
|
||
return true
|
||
}
|
||
|
||
/**
|
||
* Вспомогательные функции для форматирования
|
||
*/
|
||
private getSlackColor(severity: string): string {
|
||
const colors: Record<string, string> = {
|
||
CRITICAL: '#FF0000',
|
||
HIGH: '#FF8C00',
|
||
MEDIUM: '#FFD700',
|
||
LOW: '#00FF00',
|
||
}
|
||
return colors[severity] || '#808080'
|
||
}
|
||
|
||
private getTeamsColor(severity: string): string {
|
||
const colors: Record<string, string> = {
|
||
CRITICAL: 'FF0000',
|
||
HIGH: 'FF8C00',
|
||
MEDIUM: 'FFD700',
|
||
LOW: '00FF00',
|
||
}
|
||
return colors[severity] || '808080'
|
||
}
|
||
|
||
private mapCEFSeverity(severity: string): number {
|
||
const mapping: Record<string, number> = {
|
||
LOW: 3,
|
||
MEDIUM: 5,
|
||
HIGH: 7,
|
||
CRITICAL: 10,
|
||
}
|
||
return mapping[severity] || 5
|
||
}
|
||
|
||
private mapSyslogPriority(severity: string): number {
|
||
const mapping: Record<string, number> = {
|
||
LOW: 22, // local0.info
|
||
MEDIUM: 20, // local0.warning
|
||
HIGH: 19, // local0.error
|
||
CRITICAL: 18, // local0.critical
|
||
}
|
||
return mapping[severity] || 20
|
||
}
|
||
} |