Add cross-service alert correlation system FRE-4500
- Unified alert types (AlertSource, AlertCategory, CorrelationStatus, EntityType) - NormalizedAlert and CorrelationGroup Prisma models - AlertNormalizer for all 4 services (DarkWatch, SpamShield, VoicePrint, CallAnalysis) - CorrelationEngine with temporal + entity-based correlation detection - CorrelationService orchestrator with dashboard API - Correlation API routes (/api/v1/correlation/*) - Service emitters wired to DarkWatch, SpamShield, VoicePrint - pnpm workspace config for monorepo
This commit is contained in:
143
packages/correlation/src/service.ts
Normal file
143
packages/correlation/src/service.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import {
|
||||
AlertSource,
|
||||
AlertCategory,
|
||||
Severity,
|
||||
EntityType,
|
||||
NormalizedAlertInput,
|
||||
CorrelationGroupOutput,
|
||||
CorrelatedAlertOutput,
|
||||
CorrelationQuery,
|
||||
} from "@shieldai/types";
|
||||
import { alertNormalizer, AlertNormalizer } from "./normalizer";
|
||||
import { correlationEngine, CorrelationEngine } from "./engine";
|
||||
|
||||
export class CorrelationService {
|
||||
private normalizer: AlertNormalizer;
|
||||
private engine: CorrelationEngine;
|
||||
|
||||
constructor(
|
||||
normalizer: AlertNormalizer = alertNormalizer,
|
||||
engine: CorrelationEngine = correlationEngine
|
||||
) {
|
||||
this.normalizer = normalizer;
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
public async ingestDarkWatchAlert(
|
||||
userId: string,
|
||||
sourceAlertId: string,
|
||||
payload: {
|
||||
exposureId: string;
|
||||
breachName: string;
|
||||
severity: string;
|
||||
channel: string;
|
||||
dataType?: string[];
|
||||
dataSource?: string;
|
||||
},
|
||||
timestamp?: Date
|
||||
): Promise<CorrelatedAlertOutput> {
|
||||
const normalized = this.normalizer.normalizeDarkWatchAlert(
|
||||
userId,
|
||||
sourceAlertId,
|
||||
payload,
|
||||
timestamp
|
||||
);
|
||||
return this.engine.ingestAlert(normalized);
|
||||
}
|
||||
|
||||
public async ingestSpamShieldAlert(
|
||||
userId: string,
|
||||
sourceAlertId: string,
|
||||
payload: {
|
||||
phoneNumber: string;
|
||||
decision: string;
|
||||
confidence: number;
|
||||
reasons?: string[];
|
||||
channel?: "call" | "sms";
|
||||
hiyaReputationScore?: number;
|
||||
truecallerSpamScore?: number;
|
||||
},
|
||||
timestamp?: Date
|
||||
): Promise<CorrelatedAlertOutput> {
|
||||
const normalized = this.normalizer.normalizeSpamShieldAlert(
|
||||
userId,
|
||||
sourceAlertId,
|
||||
payload,
|
||||
timestamp
|
||||
);
|
||||
return this.engine.ingestAlert(normalized);
|
||||
}
|
||||
|
||||
public async ingestVoicePrintAlert(
|
||||
userId: string,
|
||||
sourceAlertId: string,
|
||||
payload: {
|
||||
jobId: string;
|
||||
verdict: string;
|
||||
syntheticScore: number;
|
||||
confidence: number;
|
||||
matchedEnrollmentId?: string;
|
||||
matchedSimilarity?: number;
|
||||
analysisType?: string;
|
||||
},
|
||||
timestamp?: Date
|
||||
): Promise<CorrelatedAlertOutput> {
|
||||
const normalized = this.normalizer.normalizeVoicePrintAlert(
|
||||
userId,
|
||||
sourceAlertId,
|
||||
payload,
|
||||
timestamp
|
||||
);
|
||||
return this.engine.ingestAlert(normalized);
|
||||
}
|
||||
|
||||
public async ingestCallAnalysisAlert(
|
||||
userId: string,
|
||||
sourceAlertId: string,
|
||||
payload: {
|
||||
callId: string;
|
||||
eventType?: string;
|
||||
mosScore?: number;
|
||||
anomaly?: string;
|
||||
sentiment?: { label: string; score: number };
|
||||
},
|
||||
timestamp?: Date
|
||||
): Promise<CorrelatedAlertOutput> {
|
||||
const normalized = this.normalizer.normalizeCallAnalysisAlert(
|
||||
userId,
|
||||
sourceAlertId,
|
||||
payload,
|
||||
timestamp
|
||||
);
|
||||
return this.engine.ingestAlert(normalized);
|
||||
}
|
||||
|
||||
public async ingestGenericAlert(
|
||||
input: NormalizedAlertInput
|
||||
): Promise<CorrelatedAlertOutput> {
|
||||
return this.engine.ingestAlert(input);
|
||||
}
|
||||
|
||||
public getCorrelatedAlerts(query: CorrelationQuery) {
|
||||
return this.engine.getCorrelatedAlerts(query);
|
||||
}
|
||||
|
||||
public getCorrelationGroups(query: CorrelationQuery) {
|
||||
return this.engine.getCorrelationGroups(query);
|
||||
}
|
||||
|
||||
public getGroupById(groupId: string) {
|
||||
return this.engine.getGroupById(groupId);
|
||||
}
|
||||
|
||||
public resolveGroup(groupId: string, status?: string) {
|
||||
return this.engine.resolveGroup(groupId, status as any);
|
||||
}
|
||||
|
||||
public getDashboardData(userId: string, timeWindowMinutes?: number) {
|
||||
return this.engine.getDashboardData(userId, timeWindowMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
export const correlationService = new CorrelationService();
|
||||
export { alertNormalizer, correlationEngine };
|
||||
Reference in New Issue
Block a user