guardrail-compliance 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/emitter.d.ts +97 -0
- package/dist/audit/emitter.d.ts.map +1 -0
- package/dist/audit/emitter.js +197 -0
- package/dist/audit/events.d.ts +304 -0
- package/dist/audit/events.d.ts.map +1 -0
- package/dist/audit/events.js +267 -0
- package/dist/audit/index.d.ts +11 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +51 -0
- package/dist/audit/storage.d.ts +93 -0
- package/dist/audit/storage.d.ts.map +1 -0
- package/dist/audit/storage.js +337 -0
- package/dist/automation/__tests__/compliance-scheduler.test.d.ts +2 -0
- package/dist/automation/__tests__/compliance-scheduler.test.d.ts.map +1 -0
- package/dist/automation/__tests__/compliance-scheduler.test.js +140 -0
- package/dist/automation/audit-logger.d.ts +129 -0
- package/dist/automation/audit-logger.d.ts.map +1 -0
- package/dist/automation/audit-logger.js +473 -0
- package/dist/automation/compliance-scheduler-fixed.d.ts +1 -0
- package/dist/automation/compliance-scheduler-fixed.d.ts.map +1 -0
- package/dist/automation/compliance-scheduler-fixed.js +1 -0
- package/dist/automation/compliance-scheduler.d.ts +83 -0
- package/dist/automation/compliance-scheduler.d.ts.map +1 -0
- package/dist/automation/compliance-scheduler.js +414 -0
- package/dist/automation/dashboard.d.ts +194 -0
- package/dist/automation/dashboard.d.ts.map +1 -0
- package/dist/automation/dashboard.js +768 -0
- package/dist/automation/email-service.d.ts +69 -0
- package/dist/automation/email-service.d.ts.map +1 -0
- package/dist/automation/email-service.js +218 -0
- package/dist/automation/evidence-collector.d.ts +140 -0
- package/dist/automation/evidence-collector.d.ts.map +1 -0
- package/dist/automation/evidence-collector.js +682 -0
- package/dist/automation/index.d.ts +8 -0
- package/dist/automation/index.d.ts.map +1 -0
- package/dist/automation/index.js +24 -0
- package/dist/automation/pdf-exporter.d.ts +90 -0
- package/dist/automation/pdf-exporter.d.ts.map +1 -0
- package/dist/automation/pdf-exporter.js +381 -0
- package/dist/automation/reporting-engine.d.ts +116 -0
- package/dist/automation/reporting-engine.d.ts.map +1 -0
- package/dist/automation/reporting-engine.js +329 -0
- package/dist/container/index.d.ts +4 -0
- package/dist/container/index.d.ts.map +1 -0
- package/dist/container/index.js +19 -0
- package/dist/container/kubernetes.d.ts +94 -0
- package/dist/container/kubernetes.d.ts.map +1 -0
- package/dist/container/kubernetes.js +268 -0
- package/dist/container/rules.d.ts +27 -0
- package/dist/container/rules.d.ts.map +1 -0
- package/dist/container/rules.js +216 -0
- package/dist/container/scanner.d.ts +50 -0
- package/dist/container/scanner.d.ts.map +1 -0
- package/dist/container/scanner.js +143 -0
- package/dist/frameworks/engine.d.ts +108 -0
- package/dist/frameworks/engine.d.ts.map +1 -0
- package/dist/frameworks/engine.js +206 -0
- package/dist/frameworks/gdpr.d.ts +6 -0
- package/dist/frameworks/gdpr.d.ts.map +1 -0
- package/dist/frameworks/gdpr.js +198 -0
- package/dist/frameworks/hipaa.d.ts +6 -0
- package/dist/frameworks/hipaa.d.ts.map +1 -0
- package/dist/frameworks/hipaa.js +183 -0
- package/dist/frameworks/index.d.ts +8 -0
- package/dist/frameworks/index.d.ts.map +1 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/iso27001.d.ts +63 -0
- package/dist/frameworks/iso27001.d.ts.map +1 -0
- package/dist/frameworks/iso27001.js +331 -0
- package/dist/frameworks/nist.d.ts +62 -0
- package/dist/frameworks/nist.d.ts.map +1 -0
- package/dist/frameworks/nist.js +424 -0
- package/dist/frameworks/pci.d.ts +6 -0
- package/dist/frameworks/pci.d.ts.map +1 -0
- package/dist/frameworks/pci.js +201 -0
- package/dist/frameworks/soc2.d.ts +7 -0
- package/dist/frameworks/soc2.d.ts.map +1 -0
- package/dist/frameworks/soc2.js +248 -0
- package/dist/iac/drift-detector.d.ts +64 -0
- package/dist/iac/drift-detector.d.ts.map +1 -0
- package/dist/iac/drift-detector.js +134 -0
- package/dist/iac/index.d.ts +4 -0
- package/dist/iac/index.d.ts.map +1 -0
- package/dist/iac/index.js +19 -0
- package/dist/iac/rules.d.ts +17 -0
- package/dist/iac/rules.d.ts.map +1 -0
- package/dist/iac/rules.js +385 -0
- package/dist/iac/scanner.d.ts +104 -0
- package/dist/iac/scanner.d.ts.map +1 -0
- package/dist/iac/scanner.js +343 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/pii/data-flow.d.ts +58 -0
- package/dist/pii/data-flow.d.ts.map +1 -0
- package/dist/pii/data-flow.js +154 -0
- package/dist/pii/detector.d.ts +60 -0
- package/dist/pii/detector.d.ts.map +1 -0
- package/dist/pii/detector.js +267 -0
- package/dist/pii/index.d.ts +4 -0
- package/dist/pii/index.d.ts.map +1 -0
- package/dist/pii/index.js +19 -0
- package/dist/pii/patterns.d.ts +36 -0
- package/dist/pii/patterns.d.ts.map +1 -0
- package/dist/pii/patterns.js +108 -0
- package/dist/policy/index.d.ts +5 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +20 -0
- package/dist/policy/opa-engine.d.ts +121 -0
- package/dist/policy/opa-engine.d.ts.map +1 -0
- package/dist/policy/opa-engine.js +423 -0
- package/package.json +31 -0
- package/src/audit/emitter.ts +383 -0
- package/src/audit/events.ts +351 -0
- package/src/audit/index.ts +35 -0
- package/src/audit/storage.ts +394 -0
- package/src/automation/__tests__/compliance-scheduler.test.ts +183 -0
- package/src/automation/audit-logger.ts +629 -0
- package/src/automation/compliance-scheduler-fixed.ts +0 -0
- package/src/automation/compliance-scheduler.ts +516 -0
- package/src/automation/dashboard.ts +947 -0
- package/src/automation/email-service.ts +230 -0
- package/src/automation/evidence-collector.ts +866 -0
- package/src/automation/index.ts +8 -0
- package/src/automation/pdf-exporter.ts +434 -0
- package/src/automation/reporting-engine.ts +462 -0
- package/src/container/index.ts +3 -0
- package/src/container/kubernetes.ts +379 -0
- package/src/container/rules.ts +244 -0
- package/src/container/scanner.ts +202 -0
- package/src/frameworks/engine.ts +298 -0
- package/src/frameworks/gdpr.ts +204 -0
- package/src/frameworks/hipaa.ts +209 -0
- package/src/frameworks/index.ts +23 -0
- package/src/frameworks/iso27001.ts +398 -0
- package/src/frameworks/nist.ts +518 -0
- package/src/frameworks/pci.ts +226 -0
- package/src/frameworks/soc2.ts +281 -0
- package/src/iac/drift-detector.ts +197 -0
- package/src/iac/index.ts +3 -0
- package/src/iac/rules.ts +420 -0
- package/src/iac/scanner.ts +445 -0
- package/src/index.ts +17 -0
- package/src/pii/data-flow.ts +216 -0
- package/src/pii/detector.ts +327 -0
- package/src/pii/index.ts +3 -0
- package/src/pii/patterns.ts +128 -0
- package/src/policy/index.ts +5 -0
- package/src/policy/opa-engine.ts +504 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import { prisma } from "@guardrail/database";
|
|
2
|
+
import { complianceAutomationEngine } from "../frameworks/engine";
|
|
3
|
+
import { evidenceCollector } from "./evidence-collector";
|
|
4
|
+
import { reportingEngine } from "./reporting-engine";
|
|
5
|
+
import { emailService } from "./email-service";
|
|
6
|
+
// import { auditLogger } from './audit-logger'; // Currently unused
|
|
7
|
+
// import { CronJob } from 'cron'; // Commented out until dependency is installed
|
|
8
|
+
|
|
9
|
+
interface ComplianceSchedule {
|
|
10
|
+
id: string;
|
|
11
|
+
projectId: string;
|
|
12
|
+
frameworkId: string;
|
|
13
|
+
frequency: string; // Cron expression
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
lastRun?: Date;
|
|
16
|
+
nextRun?: Date;
|
|
17
|
+
notifications?: {
|
|
18
|
+
email?: string[];
|
|
19
|
+
slack?: string;
|
|
20
|
+
webhook?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ComplianceExecutionResult {
|
|
25
|
+
scheduleId: string;
|
|
26
|
+
executionId: string;
|
|
27
|
+
startTime: Date;
|
|
28
|
+
endTime: Date;
|
|
29
|
+
status: "running" | "completed" | "failed";
|
|
30
|
+
result?: {
|
|
31
|
+
assessment?: any;
|
|
32
|
+
evidence?: any;
|
|
33
|
+
report?: any;
|
|
34
|
+
};
|
|
35
|
+
error?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Compliance Scheduler
|
|
40
|
+
*
|
|
41
|
+
* Manages scheduled compliance checks and notifications
|
|
42
|
+
*/
|
|
43
|
+
export class ComplianceScheduler {
|
|
44
|
+
private jobs = new Map<string, any>();
|
|
45
|
+
private executions = new Map<string, ComplianceExecutionResult>();
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Initialize scheduler and load existing schedules
|
|
49
|
+
*/
|
|
50
|
+
async initialize(): Promise<void> {
|
|
51
|
+
const schedules = await prisma.complianceSchedule.findMany({
|
|
52
|
+
where: { enabled: true },
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
for (const schedule of schedules) {
|
|
56
|
+
await this.scheduleJob(schedule);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create or update a schedule
|
|
62
|
+
*/
|
|
63
|
+
async upsertSchedule(
|
|
64
|
+
schedule: Omit<ComplianceSchedule, "id" | "lastRun" | "nextRun">,
|
|
65
|
+
): Promise<string> {
|
|
66
|
+
if (!this.isValidCron(schedule.frequency)) {
|
|
67
|
+
throw new Error("Invalid cron expression");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let dbSchedule: any;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
dbSchedule = await prisma.complianceSchedule.upsert({
|
|
74
|
+
where: {
|
|
75
|
+
id: `${schedule.projectId}_${schedule.frameworkId}`,
|
|
76
|
+
},
|
|
77
|
+
update: {
|
|
78
|
+
schedule: schedule.frequency as any,
|
|
79
|
+
enabled: schedule.enabled,
|
|
80
|
+
},
|
|
81
|
+
create: {
|
|
82
|
+
projectId: schedule.projectId,
|
|
83
|
+
frameworkId: schedule.frameworkId,
|
|
84
|
+
schedule: schedule.frequency as any,
|
|
85
|
+
enabled: schedule.enabled,
|
|
86
|
+
nextRun: new Date(),
|
|
87
|
+
} as any,
|
|
88
|
+
});
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.warn("Could not upsert schedule in database:", error);
|
|
91
|
+
// Create a mock schedule object for in-memory operation
|
|
92
|
+
dbSchedule = {
|
|
93
|
+
id: `sched_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
94
|
+
...schedule,
|
|
95
|
+
lastRun: null,
|
|
96
|
+
nextRun: new Date(),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (schedule.enabled) {
|
|
101
|
+
await this.scheduleJob(dbSchedule);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return dbSchedule.id;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Remove a schedule
|
|
109
|
+
*/
|
|
110
|
+
async removeSchedule(projectId: string, frameworkId: string): Promise<void> {
|
|
111
|
+
const jobKey = `${projectId}:${frameworkId}`;
|
|
112
|
+
|
|
113
|
+
if (this.jobs.has(jobKey)) {
|
|
114
|
+
this.jobs.get(jobKey)!.stop();
|
|
115
|
+
this.jobs.delete(jobKey);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
await prisma.complianceSchedule.deleteMany({
|
|
120
|
+
where: {
|
|
121
|
+
projectId,
|
|
122
|
+
frameworkId,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.warn("Could not delete schedule from database:", error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Run a compliance check
|
|
132
|
+
*/
|
|
133
|
+
async runCheck(
|
|
134
|
+
projectId: string,
|
|
135
|
+
frameworkId: string,
|
|
136
|
+
options?: {
|
|
137
|
+
collectEvidence?: boolean;
|
|
138
|
+
generateReport?: boolean;
|
|
139
|
+
notifyOnCompletion?: boolean;
|
|
140
|
+
},
|
|
141
|
+
): Promise<ComplianceExecutionResult> {
|
|
142
|
+
const executionId = `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
143
|
+
|
|
144
|
+
const result: ComplianceExecutionResult = {
|
|
145
|
+
scheduleId: `${projectId}:${frameworkId}`,
|
|
146
|
+
executionId,
|
|
147
|
+
startTime: new Date(),
|
|
148
|
+
endTime: new Date(),
|
|
149
|
+
status: "running",
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
this.executions.set(executionId, result);
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
// Get project details
|
|
156
|
+
const project = await prisma.project.findUnique({
|
|
157
|
+
where: { id: projectId },
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (!project) {
|
|
161
|
+
throw new Error(`Project ${projectId} not found`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Run compliance assessment
|
|
165
|
+
const assessment = await complianceAutomationEngine.assess(
|
|
166
|
+
project.path || "",
|
|
167
|
+
frameworkId,
|
|
168
|
+
projectId,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Collect evidence if requested
|
|
172
|
+
let evidence: any = null;
|
|
173
|
+
if (options?.collectEvidence !== false) {
|
|
174
|
+
evidence = await evidenceCollector.collectForAssessment(
|
|
175
|
+
projectId,
|
|
176
|
+
frameworkId,
|
|
177
|
+
assessment,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Generate report if requested
|
|
182
|
+
let report: any = null;
|
|
183
|
+
if (options?.generateReport) {
|
|
184
|
+
report = await reportingEngine.generateReport({
|
|
185
|
+
projectId,
|
|
186
|
+
frameworkId,
|
|
187
|
+
type: "compliance",
|
|
188
|
+
format: "json",
|
|
189
|
+
includeEvidence: !!evidence,
|
|
190
|
+
includeRecommendations: true,
|
|
191
|
+
includeCharts: false,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Update result
|
|
196
|
+
result.result = {
|
|
197
|
+
assessment,
|
|
198
|
+
evidence,
|
|
199
|
+
report,
|
|
200
|
+
};
|
|
201
|
+
result.status = "completed";
|
|
202
|
+
result.endTime = new Date();
|
|
203
|
+
|
|
204
|
+
// Send notifications if requested
|
|
205
|
+
if (options?.notifyOnCompletion) {
|
|
206
|
+
setTimeout(() => {
|
|
207
|
+
this.sendNotifications(projectId, frameworkId, result);
|
|
208
|
+
}, 1000);
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
result.status = "failed";
|
|
212
|
+
result.error = error instanceof Error ? error.message : "Unknown error";
|
|
213
|
+
result.endTime = new Date();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get execution status
|
|
221
|
+
*/
|
|
222
|
+
getExecutionStatus(
|
|
223
|
+
executionId: string,
|
|
224
|
+
): ComplianceExecutionResult | undefined {
|
|
225
|
+
return this.executions.get(executionId);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get all schedules
|
|
230
|
+
*/
|
|
231
|
+
async getSchedules(projectId?: string): Promise<ComplianceSchedule[]> {
|
|
232
|
+
const schedules = await prisma.complianceSchedule.findMany({
|
|
233
|
+
where: projectId ? { projectId } : undefined,
|
|
234
|
+
orderBy: { nextRun: "asc" },
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
return schedules.map((s: any) => ({
|
|
238
|
+
id: s.id,
|
|
239
|
+
projectId: s.projectId,
|
|
240
|
+
frameworkId: s.frameworkId,
|
|
241
|
+
frequency: (s as any).schedule,
|
|
242
|
+
enabled: s.enabled,
|
|
243
|
+
lastRun: s.lastRun || undefined,
|
|
244
|
+
nextRun: s.nextRun || undefined,
|
|
245
|
+
notifications: (s as any).notifications || undefined,
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Schedule a job
|
|
251
|
+
*/
|
|
252
|
+
private async scheduleJob(schedule: any): Promise<void> {
|
|
253
|
+
const jobKey = `${schedule.projectId}:${schedule.frameworkId}`;
|
|
254
|
+
|
|
255
|
+
// Remove existing job if any
|
|
256
|
+
if (this.jobs.has(jobKey)) {
|
|
257
|
+
this.jobs.get(jobKey)!.stop();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Create new cron job
|
|
261
|
+
// const job = new CronJob(schedule.schedule, async () => {
|
|
262
|
+
// await this.executeScheduledCheck(schedule);
|
|
263
|
+
// }, null, true, 'UTC');
|
|
264
|
+
|
|
265
|
+
// this.jobs.set(jobKey, job);
|
|
266
|
+
|
|
267
|
+
// Update next run time
|
|
268
|
+
try {
|
|
269
|
+
await prisma.complianceSchedule.update({
|
|
270
|
+
where: { id: schedule.id },
|
|
271
|
+
data: { nextRun: new Date() },
|
|
272
|
+
});
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.warn("Could not update next run time in database:", error);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Execute a scheduled check - currently unused
|
|
279
|
+
/*
|
|
280
|
+
private async executeScheduledCheck(schedule: any): Promise<void> {
|
|
281
|
+
try {
|
|
282
|
+
const result = await this.runCheck(
|
|
283
|
+
schedule.projectId,
|
|
284
|
+
schedule.frameworkId,
|
|
285
|
+
{
|
|
286
|
+
collectEvidence: true,
|
|
287
|
+
generateReport: true,
|
|
288
|
+
notifyOnCompletion: true
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Update last run time
|
|
293
|
+
try {
|
|
294
|
+
await prisma.complianceSchedule.update({
|
|
295
|
+
where: { id: schedule.id },
|
|
296
|
+
data: { lastRun: new Date() }
|
|
297
|
+
});
|
|
298
|
+
} catch (error) {
|
|
299
|
+
console.warn('Could not update last run time in database:', error);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Check for compliance failures and send alerts
|
|
303
|
+
if (result.result?.assessment?.summary?.score < 70) {
|
|
304
|
+
await this.sendAlert(schedule, result);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.error(`Scheduled check failed for ${schedule.projectId}:${schedule.frameworkId}:`, error);
|
|
309
|
+
await this.sendErrorAlert(schedule, error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
*/
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Send notifications for completed checks
|
|
316
|
+
*/
|
|
317
|
+
private async sendNotifications(
|
|
318
|
+
projectId: string,
|
|
319
|
+
frameworkId: string,
|
|
320
|
+
result: ComplianceExecutionResult,
|
|
321
|
+
): Promise<void> {
|
|
322
|
+
try {
|
|
323
|
+
const schedule = await prisma.complianceSchedule.findFirst({
|
|
324
|
+
where: {
|
|
325
|
+
projectId,
|
|
326
|
+
frameworkId,
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
if (!(schedule as any)?.notifications) return;
|
|
331
|
+
|
|
332
|
+
const notifications = (schedule as any).notifications;
|
|
333
|
+
|
|
334
|
+
// Send email notifications
|
|
335
|
+
if (notifications.email?.length) {
|
|
336
|
+
const score = result.result?.assessment?.summary?.score;
|
|
337
|
+
const status = result.status === "completed" ? "completed" : "failed";
|
|
338
|
+
|
|
339
|
+
const emailResult = await emailService.sendComplianceNotification(
|
|
340
|
+
notifications.email,
|
|
341
|
+
projectId,
|
|
342
|
+
frameworkId,
|
|
343
|
+
{
|
|
344
|
+
status,
|
|
345
|
+
score,
|
|
346
|
+
summary: result.error || undefined,
|
|
347
|
+
},
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
if (!emailResult.success) {
|
|
351
|
+
console.error(
|
|
352
|
+
`Failed to send compliance email to ${notifications.email.join(", ")}: ${emailResult.error}`,
|
|
353
|
+
);
|
|
354
|
+
} else {
|
|
355
|
+
console.log(
|
|
356
|
+
`Sent compliance check email to ${notifications.email.join(", ")} (messageId: ${emailResult.messageId})`,
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Send Slack notifications
|
|
362
|
+
if (notifications.slack) {
|
|
363
|
+
try {
|
|
364
|
+
const score = result.result?.assessment?.summary?.score || 0;
|
|
365
|
+
const status =
|
|
366
|
+
score >= 90 ? "passed" : score >= 70 ? "warning" : "failed";
|
|
367
|
+
const statusIcon =
|
|
368
|
+
status === "passed" ? "✅" : status === "warning" ? "⚠️" : "❌";
|
|
369
|
+
|
|
370
|
+
const slackPayload = {
|
|
371
|
+
blocks: [
|
|
372
|
+
{
|
|
373
|
+
type: "header",
|
|
374
|
+
text: {
|
|
375
|
+
type: "plain_text",
|
|
376
|
+
text: `${statusIcon} Compliance Check ${status.toUpperCase()}`,
|
|
377
|
+
emoji: true,
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
type: "section",
|
|
382
|
+
fields: [
|
|
383
|
+
{
|
|
384
|
+
type: "mrkdwn",
|
|
385
|
+
text: `*Project:*\n${projectId}`,
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
type: "mrkdwn",
|
|
389
|
+
text: `*Framework:*\n${frameworkId}`,
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
type: "section",
|
|
395
|
+
fields: [
|
|
396
|
+
{
|
|
397
|
+
type: "mrkdwn",
|
|
398
|
+
text: `*Score:*\n${score}%`,
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
type: "mrkdwn",
|
|
402
|
+
text: `*Status:*\n${status}`,
|
|
403
|
+
},
|
|
404
|
+
],
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
type: "section",
|
|
408
|
+
text: {
|
|
409
|
+
type: "mrkdwn",
|
|
410
|
+
text: `Check completed at ${new Date().toLocaleString()}`,
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
await fetch(notifications.slack, {
|
|
417
|
+
method: "POST",
|
|
418
|
+
headers: { "Content-Type": "application/json" },
|
|
419
|
+
body: JSON.stringify(slackPayload),
|
|
420
|
+
});
|
|
421
|
+
console.log(`Sending Slack notification to ${notifications.slack}`);
|
|
422
|
+
} catch (slackError) {
|
|
423
|
+
console.error("Failed to send Slack notification:", slackError);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Send webhook notifications
|
|
428
|
+
if (notifications.webhook) {
|
|
429
|
+
console.log(`Sending webhook notification to ${notifications.webhook}`);
|
|
430
|
+
try {
|
|
431
|
+
const response = await fetch(notifications.webhook, {
|
|
432
|
+
method: "POST",
|
|
433
|
+
headers: {
|
|
434
|
+
"Content-Type": "application/json",
|
|
435
|
+
},
|
|
436
|
+
body: JSON.stringify(result),
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
if (!response.ok) {
|
|
440
|
+
console.error(
|
|
441
|
+
`Failed to send webhook notification: ${response.status} ${response.statusText}`,
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
} catch (webhookError) {
|
|
445
|
+
console.error("Error sending webhook notification:", webhookError);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
} catch (error) {
|
|
449
|
+
console.error("Failed to send notifications:", error);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/*
|
|
454
|
+
private async sendAlert(schedule: any, result: ComplianceExecutionResult): Promise<void> {
|
|
455
|
+
const score = result.result?.assessment?.summary?.score || 0;
|
|
456
|
+
const message = `Compliance check failed for project ${schedule.projectId} (${schedule.frameworkId}). Score: ${score}%`;
|
|
457
|
+
|
|
458
|
+
await auditLogger.logEvent({
|
|
459
|
+
type: 'compliance_failure',
|
|
460
|
+
category: 'compliance',
|
|
461
|
+
projectId: schedule.projectId,
|
|
462
|
+
timestamp: new Date(),
|
|
463
|
+
severity: 'high',
|
|
464
|
+
source: 'scheduler',
|
|
465
|
+
details: {
|
|
466
|
+
action: 'Compliance check failed',
|
|
467
|
+
framework: schedule.frameworkId,
|
|
468
|
+
score,
|
|
469
|
+
message
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
await this.sendNotifications(schedule.projectId, schedule.frameworkId, result);
|
|
474
|
+
}
|
|
475
|
+
*/
|
|
476
|
+
|
|
477
|
+
/*
|
|
478
|
+
private async sendErrorAlert(schedule: any, error: any): Promise<void> {
|
|
479
|
+
await auditLogger.logEvent({
|
|
480
|
+
type: 'compliance_error',
|
|
481
|
+
category: 'system',
|
|
482
|
+
projectId: schedule.projectId,
|
|
483
|
+
timestamp: new Date(),
|
|
484
|
+
severity: 'critical',
|
|
485
|
+
source: 'scheduler',
|
|
486
|
+
details: {
|
|
487
|
+
action: 'Compliance check error',
|
|
488
|
+
framework: schedule.frameworkId,
|
|
489
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
*/
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Validate cron expression
|
|
497
|
+
*/
|
|
498
|
+
private isValidCron(cron: string): boolean {
|
|
499
|
+
// Basic validation - should be more sophisticated
|
|
500
|
+
const parts = cron.split(" ");
|
|
501
|
+
return parts.length === 5;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Stop all jobs
|
|
506
|
+
*/
|
|
507
|
+
stopAll(): void {
|
|
508
|
+
for (const job of this.jobs.values()) {
|
|
509
|
+
job.stop();
|
|
510
|
+
}
|
|
511
|
+
this.jobs.clear();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Export singleton instance
|
|
516
|
+
export const complianceScheduler = new ComplianceScheduler();
|