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,629 @@
|
|
|
1
|
+
import { prisma } from '@guardrail/database';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
|
|
4
|
+
interface AuditEvent {
|
|
5
|
+
id?: string;
|
|
6
|
+
type: string;
|
|
7
|
+
category: 'compliance' | 'security' | 'access' | 'data' | 'system';
|
|
8
|
+
projectId?: string;
|
|
9
|
+
frameworkId?: string;
|
|
10
|
+
userId?: string;
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
timestamp: Date;
|
|
13
|
+
metadata?: any;
|
|
14
|
+
details?: any;
|
|
15
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
16
|
+
source: string;
|
|
17
|
+
correlationId?: string;
|
|
18
|
+
ipAddress?: string;
|
|
19
|
+
userAgent?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface AuditQuery {
|
|
23
|
+
projectId?: string;
|
|
24
|
+
frameworkId?: string;
|
|
25
|
+
userId?: string;
|
|
26
|
+
type?: string;
|
|
27
|
+
category?: string;
|
|
28
|
+
severity?: string;
|
|
29
|
+
startDate?: Date;
|
|
30
|
+
endDate?: Date;
|
|
31
|
+
limit?: number;
|
|
32
|
+
offset?: number;
|
|
33
|
+
orderBy?: 'timestamp' | 'severity' | 'type';
|
|
34
|
+
orderDirection?: 'asc' | 'desc';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface AuditTrail {
|
|
38
|
+
events: AuditEvent[];
|
|
39
|
+
summary: {
|
|
40
|
+
totalEvents: number;
|
|
41
|
+
byType: Record<string, number>;
|
|
42
|
+
byCategory: Record<string, number>;
|
|
43
|
+
bySeverity: Record<string, number>;
|
|
44
|
+
timeRange: {
|
|
45
|
+
start: Date;
|
|
46
|
+
end: Date;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
metadata: {
|
|
50
|
+
hasMore: boolean;
|
|
51
|
+
totalCount: number;
|
|
52
|
+
query: AuditQuery;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Comprehensive Audit Trail Logger
|
|
58
|
+
*
|
|
59
|
+
* Provides tamper-proof logging of all compliance-related activities
|
|
60
|
+
* with chain of custody verification and evidence preservation
|
|
61
|
+
*/
|
|
62
|
+
export class AuditLogger {
|
|
63
|
+
private readonly sequenceCounters: Map<string, number> = new Map();
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Log an audit event
|
|
67
|
+
*/
|
|
68
|
+
async logEvent(event: AuditEvent): Promise<string> {
|
|
69
|
+
// Generate unique ID if not provided
|
|
70
|
+
if (!event.id) {
|
|
71
|
+
event.id = `audit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get sequence number for ordering
|
|
75
|
+
const sequenceKey = event.projectId || 'global';
|
|
76
|
+
const sequenceNumber = (this.sequenceCounters.get(sequenceKey) || 0) + 1;
|
|
77
|
+
this.sequenceCounters.set(sequenceKey, sequenceNumber);
|
|
78
|
+
|
|
79
|
+
// Get previous hash for chain integrity
|
|
80
|
+
let previousHash = '';
|
|
81
|
+
try {
|
|
82
|
+
const previousEvent = await prisma.auditEvent.findFirst({
|
|
83
|
+
where: event.projectId ? { projectId: event.projectId } : {},
|
|
84
|
+
orderBy: { timestamp: 'desc' }
|
|
85
|
+
});
|
|
86
|
+
previousHash = (previousEvent as any)?.hash || '';
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.warn('Could not get previous audit event:', error);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Calculate current hash
|
|
92
|
+
const currentHash = this.calculateHash({
|
|
93
|
+
...event,
|
|
94
|
+
sequenceNumber,
|
|
95
|
+
previousHash
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Store in database
|
|
99
|
+
await this.storeEvent({
|
|
100
|
+
...event,
|
|
101
|
+
sequenceNumber,
|
|
102
|
+
hash: currentHash,
|
|
103
|
+
previousHash: previousHash || null
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Also log to external systems for redundancy
|
|
107
|
+
await this.logToExternalSystems(event);
|
|
108
|
+
|
|
109
|
+
return event.id || '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Log compliance check start
|
|
114
|
+
*/
|
|
115
|
+
async logComplianceCheckStart(
|
|
116
|
+
projectId: string,
|
|
117
|
+
frameworkId: string,
|
|
118
|
+
executionId: string,
|
|
119
|
+
metadata?: any
|
|
120
|
+
): Promise<string> {
|
|
121
|
+
return this.logEvent({
|
|
122
|
+
type: 'compliance_check_started',
|
|
123
|
+
category: 'compliance',
|
|
124
|
+
projectId,
|
|
125
|
+
frameworkId,
|
|
126
|
+
timestamp: new Date(),
|
|
127
|
+
severity: 'low',
|
|
128
|
+
source: 'compliance-engine',
|
|
129
|
+
correlationId: executionId,
|
|
130
|
+
metadata: {
|
|
131
|
+
executionId,
|
|
132
|
+
...metadata
|
|
133
|
+
},
|
|
134
|
+
details: {
|
|
135
|
+
action: 'Compliance assessment initiated',
|
|
136
|
+
framework: frameworkId,
|
|
137
|
+
project: projectId
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Log compliance check completion
|
|
144
|
+
*/
|
|
145
|
+
async logComplianceCheckComplete(
|
|
146
|
+
projectId: string,
|
|
147
|
+
frameworkId: string,
|
|
148
|
+
executionId: string,
|
|
149
|
+
result: any,
|
|
150
|
+
metadata?: any
|
|
151
|
+
): Promise<string> {
|
|
152
|
+
const severity = this.determineSeverity(result);
|
|
153
|
+
|
|
154
|
+
return this.logEvent({
|
|
155
|
+
type: 'compliance_check_completed',
|
|
156
|
+
category: 'compliance',
|
|
157
|
+
projectId,
|
|
158
|
+
frameworkId,
|
|
159
|
+
timestamp: new Date(),
|
|
160
|
+
severity,
|
|
161
|
+
source: 'compliance-engine',
|
|
162
|
+
correlationId: executionId,
|
|
163
|
+
metadata: {
|
|
164
|
+
executionId,
|
|
165
|
+
score: result.summary?.score,
|
|
166
|
+
compliant: result.summary?.compliant,
|
|
167
|
+
nonCompliant: result.summary?.nonCompliant,
|
|
168
|
+
...metadata
|
|
169
|
+
},
|
|
170
|
+
details: {
|
|
171
|
+
action: 'Compliance assessment completed',
|
|
172
|
+
framework: frameworkId,
|
|
173
|
+
project: projectId,
|
|
174
|
+
result: {
|
|
175
|
+
totalControls: result.summary?.totalControls,
|
|
176
|
+
score: result.summary?.score,
|
|
177
|
+
status: result.summary?.score >= 70 ? 'PASS' : 'FAIL'
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Log evidence collection
|
|
185
|
+
*/
|
|
186
|
+
async logEvidenceCollection(
|
|
187
|
+
projectId: string,
|
|
188
|
+
frameworkId: string,
|
|
189
|
+
collectionId: string,
|
|
190
|
+
artifactCount: number,
|
|
191
|
+
metadata?: any
|
|
192
|
+
): Promise<string> {
|
|
193
|
+
return this.logEvent({
|
|
194
|
+
type: 'evidence_collected',
|
|
195
|
+
category: 'compliance',
|
|
196
|
+
projectId,
|
|
197
|
+
frameworkId,
|
|
198
|
+
timestamp: new Date(),
|
|
199
|
+
severity: 'low',
|
|
200
|
+
source: 'evidence-collector',
|
|
201
|
+
correlationId: collectionId,
|
|
202
|
+
metadata: {
|
|
203
|
+
collectionId,
|
|
204
|
+
artifactCount,
|
|
205
|
+
...metadata
|
|
206
|
+
},
|
|
207
|
+
details: {
|
|
208
|
+
action: 'Evidence artifacts collected',
|
|
209
|
+
artifactCount,
|
|
210
|
+
collectionId
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Log compliance violation
|
|
217
|
+
*/
|
|
218
|
+
async logViolation(
|
|
219
|
+
projectId: string,
|
|
220
|
+
frameworkId: string,
|
|
221
|
+
controlId: string,
|
|
222
|
+
violation: any,
|
|
223
|
+
severity: 'medium' | 'high' | 'critical' = 'high'
|
|
224
|
+
): Promise<string> {
|
|
225
|
+
return this.logEvent({
|
|
226
|
+
type: 'compliance_violation',
|
|
227
|
+
category: 'compliance',
|
|
228
|
+
projectId,
|
|
229
|
+
frameworkId,
|
|
230
|
+
timestamp: new Date(),
|
|
231
|
+
severity,
|
|
232
|
+
source: 'compliance-monitor',
|
|
233
|
+
metadata: {
|
|
234
|
+
controlId,
|
|
235
|
+
violation: violation.description,
|
|
236
|
+
recommendation: violation.recommendation
|
|
237
|
+
},
|
|
238
|
+
details: {
|
|
239
|
+
action: 'Compliance violation detected',
|
|
240
|
+
controlId,
|
|
241
|
+
violation: violation.description,
|
|
242
|
+
impact: violation.impact,
|
|
243
|
+
recommendation: violation.recommendation
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Log remediation action
|
|
250
|
+
*/
|
|
251
|
+
async logRemediation(
|
|
252
|
+
projectId: string,
|
|
253
|
+
frameworkId: string,
|
|
254
|
+
controlId: string,
|
|
255
|
+
action: string,
|
|
256
|
+
userId?: string
|
|
257
|
+
): Promise<string> {
|
|
258
|
+
return this.logEvent({
|
|
259
|
+
type: 'remediation_performed',
|
|
260
|
+
category: 'compliance',
|
|
261
|
+
projectId,
|
|
262
|
+
frameworkId,
|
|
263
|
+
userId,
|
|
264
|
+
timestamp: new Date(),
|
|
265
|
+
severity: 'medium',
|
|
266
|
+
source: 'remediation-system',
|
|
267
|
+
metadata: {
|
|
268
|
+
controlId,
|
|
269
|
+
action
|
|
270
|
+
},
|
|
271
|
+
details: {
|
|
272
|
+
action: 'Compliance remediation performed',
|
|
273
|
+
controlId,
|
|
274
|
+
remediation: action,
|
|
275
|
+
performedBy: userId || 'system'
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Log access to compliance data
|
|
282
|
+
*/
|
|
283
|
+
async logAccess(
|
|
284
|
+
projectId: string,
|
|
285
|
+
userId: string,
|
|
286
|
+
action: string,
|
|
287
|
+
resource: string,
|
|
288
|
+
metadata?: any
|
|
289
|
+
): Promise<string> {
|
|
290
|
+
return this.logEvent({
|
|
291
|
+
type: 'compliance_access',
|
|
292
|
+
category: 'access',
|
|
293
|
+
projectId,
|
|
294
|
+
userId,
|
|
295
|
+
timestamp: new Date(),
|
|
296
|
+
severity: 'low',
|
|
297
|
+
source: 'access-control',
|
|
298
|
+
metadata: {
|
|
299
|
+
action,
|
|
300
|
+
resource,
|
|
301
|
+
...metadata
|
|
302
|
+
},
|
|
303
|
+
details: {
|
|
304
|
+
action: 'Compliance data accessed',
|
|
305
|
+
resource,
|
|
306
|
+
performedBy: userId
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Query audit trail
|
|
313
|
+
*/
|
|
314
|
+
async query(query: AuditQuery): Promise<AuditTrail> {
|
|
315
|
+
// Build where clause
|
|
316
|
+
const where: any = {};
|
|
317
|
+
|
|
318
|
+
if (query.projectId) where.projectId = query.projectId;
|
|
319
|
+
if (query.frameworkId) where.frameworkId = query.frameworkId;
|
|
320
|
+
if (query.userId) where.userId = query.userId;
|
|
321
|
+
if (query.type) where.type = query.type;
|
|
322
|
+
if (query.category) where.category = query.category;
|
|
323
|
+
if (query.severity) where.severity = query.severity.toUpperCase();
|
|
324
|
+
if (query.startDate || query.endDate) {
|
|
325
|
+
where.timestamp = {};
|
|
326
|
+
if (query.startDate) where.timestamp.gte = query.startDate;
|
|
327
|
+
if (query.endDate) where.timestamp.lte = query.endDate;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Get total count
|
|
331
|
+
let totalCount = 0;
|
|
332
|
+
try {
|
|
333
|
+
totalCount = await prisma.auditEvent.count({ where });
|
|
334
|
+
} catch (error) {
|
|
335
|
+
console.warn('Could not count audit events:', error);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Get events
|
|
339
|
+
let events: any[] = [];
|
|
340
|
+
try {
|
|
341
|
+
events = await prisma.auditEvent.findMany({
|
|
342
|
+
where,
|
|
343
|
+
orderBy: {
|
|
344
|
+
[query.orderBy || 'timestamp']: query.orderDirection || 'desc'
|
|
345
|
+
},
|
|
346
|
+
take: query.limit || 100,
|
|
347
|
+
skip: query.offset || 0
|
|
348
|
+
});
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.warn('Could not fetch audit events:', error);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Transform events
|
|
354
|
+
const auditEvents: AuditEvent[] = events.map(e => ({
|
|
355
|
+
id: e.id,
|
|
356
|
+
type: e.type,
|
|
357
|
+
category: e.category.toLowerCase() as any,
|
|
358
|
+
projectId: e.projectId || undefined,
|
|
359
|
+
frameworkId: e.frameworkId || undefined,
|
|
360
|
+
userId: e.userId || undefined,
|
|
361
|
+
sessionId: e.sessionId || undefined,
|
|
362
|
+
timestamp: e.timestamp,
|
|
363
|
+
metadata: e.metadata,
|
|
364
|
+
details: e.details,
|
|
365
|
+
severity: e.severity.toLowerCase() as any,
|
|
366
|
+
source: e.source,
|
|
367
|
+
correlationId: e.correlationId || undefined,
|
|
368
|
+
ipAddress: e.ipAddress || undefined,
|
|
369
|
+
userAgent: e.userAgent || undefined
|
|
370
|
+
}));
|
|
371
|
+
|
|
372
|
+
// Generate summary
|
|
373
|
+
const summary = this.generateSummary(auditEvents);
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
events: auditEvents,
|
|
377
|
+
summary,
|
|
378
|
+
metadata: {
|
|
379
|
+
hasMore: (query.offset || 0) + auditEvents.length < totalCount,
|
|
380
|
+
totalCount,
|
|
381
|
+
query
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Get audit trail for a specific time period
|
|
388
|
+
*/
|
|
389
|
+
async getAuditTrail(
|
|
390
|
+
projectId: string,
|
|
391
|
+
startDate: Date,
|
|
392
|
+
endDate: Date
|
|
393
|
+
): Promise<AuditTrail> {
|
|
394
|
+
return this.query({
|
|
395
|
+
projectId,
|
|
396
|
+
startDate,
|
|
397
|
+
endDate,
|
|
398
|
+
orderBy: 'timestamp',
|
|
399
|
+
orderDirection: 'asc'
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Verify audit trail integrity
|
|
405
|
+
*/
|
|
406
|
+
async verifyIntegrity(projectId?: string): Promise<{
|
|
407
|
+
valid: boolean;
|
|
408
|
+
totalEvents: number;
|
|
409
|
+
violations: Array<{
|
|
410
|
+
eventId: string;
|
|
411
|
+
sequenceNumber: number;
|
|
412
|
+
issue: string;
|
|
413
|
+
}>;
|
|
414
|
+
}> {
|
|
415
|
+
let events: any[] = [];
|
|
416
|
+
try {
|
|
417
|
+
events = await prisma.auditEvent.findMany({
|
|
418
|
+
where: projectId ? { projectId } : {},
|
|
419
|
+
orderBy: { timestamp: 'asc' }
|
|
420
|
+
});
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.warn('Could not verify integrity - audit events table not available:', error);
|
|
423
|
+
return {
|
|
424
|
+
valid: false,
|
|
425
|
+
totalEvents: 0,
|
|
426
|
+
violations: [{
|
|
427
|
+
eventId: 'N/A',
|
|
428
|
+
sequenceNumber: 0,
|
|
429
|
+
issue: 'Audit events table not available'
|
|
430
|
+
}]
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const violations: any[] = [];
|
|
435
|
+
|
|
436
|
+
for (let i = 0; i < events.length; i++) {
|
|
437
|
+
const event = events[i];
|
|
438
|
+
|
|
439
|
+
// Check sequence continuity
|
|
440
|
+
if (i > 0 && (event.sequenceNumber || 0) !== (events[i - 1].sequenceNumber || 0) + 1) {
|
|
441
|
+
violations.push({
|
|
442
|
+
eventId: event.id,
|
|
443
|
+
sequenceNumber: event.sequenceNumber || 0,
|
|
444
|
+
issue: 'Sequence number gap'
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Check hash chain
|
|
449
|
+
if (i > 0) {
|
|
450
|
+
const expectedPreviousHash = events[i - 1].hash;
|
|
451
|
+
if (event.previousHash !== expectedPreviousHash) {
|
|
452
|
+
violations.push({
|
|
453
|
+
eventId: event.id,
|
|
454
|
+
sequenceNumber: event.sequenceNumber || 0,
|
|
455
|
+
issue: 'Hash chain broken'
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Verify hash integrity
|
|
461
|
+
const recalculatedHash = this.calculateHash({
|
|
462
|
+
id: event.id,
|
|
463
|
+
type: event.type,
|
|
464
|
+
category: event.category,
|
|
465
|
+
timestamp: event.timestamp,
|
|
466
|
+
sequenceNumber: event.sequenceNumber || 0,
|
|
467
|
+
previousHash: event.previousHash,
|
|
468
|
+
metadata: event.metadata,
|
|
469
|
+
details: event.details
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
if (recalculatedHash !== (event.hash || '')) {
|
|
473
|
+
violations.push({
|
|
474
|
+
eventId: event.id,
|
|
475
|
+
sequenceNumber: event.sequenceNumber || 0,
|
|
476
|
+
issue: 'Hash mismatch - possible tampering'
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
valid: violations.length === 0,
|
|
483
|
+
totalEvents: events.length,
|
|
484
|
+
violations
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Store audit event in database
|
|
490
|
+
*/
|
|
491
|
+
private async storeEvent(event: AuditEvent & { sequenceNumber?: number; hash?: string; previousHash?: string | null }): Promise<void> {
|
|
492
|
+
try {
|
|
493
|
+
await prisma.auditEvent.create({
|
|
494
|
+
data: {
|
|
495
|
+
id: event.id || '',
|
|
496
|
+
type: event.type,
|
|
497
|
+
category: event.category,
|
|
498
|
+
projectId: event.projectId as string | undefined,
|
|
499
|
+
// frameworkId not in schema
|
|
500
|
+
// frameworkId: event.frameworkId,
|
|
501
|
+
timestamp: event.timestamp,
|
|
502
|
+
// severity not in schema
|
|
503
|
+
// severity: event.severity,
|
|
504
|
+
// source not in schema
|
|
505
|
+
// source: event.source,
|
|
506
|
+
userId: event.userId,
|
|
507
|
+
metadata: event.metadata as any,
|
|
508
|
+
// recipients not in schema
|
|
509
|
+
// recipients: config.recipients as any,
|
|
510
|
+
// sequenceNumber not in schema
|
|
511
|
+
// sequenceNumber: event.sequenceNumber,
|
|
512
|
+
// hash not in schema
|
|
513
|
+
// hash: event.hash,
|
|
514
|
+
// previousHash not in schema
|
|
515
|
+
// previousHash: event.previousHash
|
|
516
|
+
} as any
|
|
517
|
+
});
|
|
518
|
+
} catch (error) {
|
|
519
|
+
console.warn('Could not store audit event in database:', error);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Calculate hash for event
|
|
525
|
+
*/
|
|
526
|
+
private calculateHash(event: any): string {
|
|
527
|
+
const hashInput = JSON.stringify({
|
|
528
|
+
id: event.id,
|
|
529
|
+
type: event.type,
|
|
530
|
+
category: event.category,
|
|
531
|
+
timestamp: event.timestamp,
|
|
532
|
+
sequenceNumber: event.sequenceNumber,
|
|
533
|
+
previousHash: event.previousHash,
|
|
534
|
+
metadata: event.metadata,
|
|
535
|
+
details: event.details
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
return createHash('sha256').update(hashInput).digest('hex');
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Determine severity based on compliance result
|
|
543
|
+
*/
|
|
544
|
+
private determineSeverity(result: any): 'low' | 'medium' | 'high' | 'critical' {
|
|
545
|
+
const score = result.summary?.score || 0;
|
|
546
|
+
|
|
547
|
+
if (score >= 90) return 'low';
|
|
548
|
+
if (score >= 70) return 'medium';
|
|
549
|
+
if (score >= 50) return 'high';
|
|
550
|
+
return 'critical';
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Log to external systems for redundancy
|
|
555
|
+
*/
|
|
556
|
+
private async logToExternalSystems(event: AuditEvent): Promise<void> {
|
|
557
|
+
// In production, integrate with:
|
|
558
|
+
// - SIEM systems (Splunk, ELK, etc.)
|
|
559
|
+
// - Cloud audit logs (AWS CloudTrail, Azure Monitor, etc.)
|
|
560
|
+
// - Immutable storage (WORM storage, blockchain)
|
|
561
|
+
// - External log aggregators
|
|
562
|
+
|
|
563
|
+
console.log(`[AUDIT] ${event.type}: ${event.category} - ${event.timestamp.toISOString()}`);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Generate summary statistics
|
|
568
|
+
*/
|
|
569
|
+
private generateSummary(events: AuditEvent[]) {
|
|
570
|
+
const byType: Record<string, number> = {};
|
|
571
|
+
const byCategory: Record<string, number> = {};
|
|
572
|
+
const bySeverity: Record<string, number> = {};
|
|
573
|
+
|
|
574
|
+
for (const event of events) {
|
|
575
|
+
byType[event.type] = (byType[event.type] || 0) + 1;
|
|
576
|
+
byCategory[event.category] = (byCategory[event.category] || 0) + 1;
|
|
577
|
+
bySeverity[event.severity] = (bySeverity[event.severity] || 0) + 1;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return {
|
|
581
|
+
totalEvents: events.length,
|
|
582
|
+
byType,
|
|
583
|
+
byCategory,
|
|
584
|
+
bySeverity,
|
|
585
|
+
timeRange: {
|
|
586
|
+
start: events.length > 0 ? events[events.length - 1]?.timestamp || new Date() : new Date(),
|
|
587
|
+
end: events.length > 0 ? events[0]?.timestamp || new Date() : new Date()
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Convert events to CSV
|
|
594
|
+
*/
|
|
595
|
+
// private convertToCSV(events: AuditEvent[]): string {
|
|
596
|
+
// // Implementation removed
|
|
597
|
+
// }
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Convert trail to XML
|
|
601
|
+
*/
|
|
602
|
+
// private convertToXML(trail: AuditTrail): string {
|
|
603
|
+
// // Implementation removed
|
|
604
|
+
// }
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Generate recommendations based on events
|
|
608
|
+
*/
|
|
609
|
+
// private generateRecommendations(events: AuditEvent[]): string[] {
|
|
610
|
+
// // Implementation removed
|
|
611
|
+
// }
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Perform detailed analysis
|
|
615
|
+
*/
|
|
616
|
+
// private performDetailedAnalysis(events: AuditEvent[]): any {
|
|
617
|
+
// // Implementation removed
|
|
618
|
+
// }
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Calculate compliance score from events
|
|
622
|
+
*/
|
|
623
|
+
// private calculateComplianceScore(events: AuditEvent[]): number {
|
|
624
|
+
// // Implementation removed
|
|
625
|
+
// }
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Export singleton instance
|
|
629
|
+
export const auditLogger = new AuditLogger();
|
|
File without changes
|