Files
Kordant/packages/correlation/src/service.ts
Senior Engineer 91e4985a8e FRE-4474 Phase 5: Verify and resolve security review findings for SpamShield and Cross-Service Correlation
- FRE-4499 (SpamShield): Verified 6 security fixes (2 High, 4 Medium)
  - S01: Pre-compiled regex in RuleEngine (ReDoS fix)
  - S02: SmsClassifier accepts senderPhoneNumber context
  - S03: AlertServer JWT auth + origin validation
  - S04: SHA-256 phone hashing (PII protection)
  - S05: DecisionEngine timeout enforcement via Promise.race
  - S06: CarrierFactory.getAllCarriers properly async/await

- FRE-4500 (Correlation): Verified 7 security fixes (2 Critical, 2 High, 2 Medium, 1 Low)
  - C1: Ingest endpoints auth via request.user.id
  - C2: IDOR protection on group endpoints (userId filter)
  - H3: JWT middleware registered in server.ts
  - H4: Fastify schema validation on all routes
  - M6: Payload sanitization with depth limit and circular ref detection
  - L7: CORS origin restricted to env var

- Resolved liveness incidents FRE-4652 and FRE-4654
- All Phase 5 child issues now complete
2026-05-02 18:36:29 -04:00

144 lines
3.6 KiB
TypeScript

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, userId: string) {
return this.engine.getGroupById(groupId, userId);
}
public resolveGroup(groupId: string, userId: string, status?: string) {
return this.engine.resolveGroup(groupId, userId, status as any);
}
public getDashboardData(userId: string, timeWindowMinutes?: number) {
return this.engine.getDashboardData(userId, timeWindowMinutes);
}
}
export const correlationService = new CorrelationService();
export { alertNormalizer, correlationEngine };