@goldensheepai/toknxr-cli 0.2.0 → 0.3.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.
@@ -0,0 +1,500 @@
1
+ /**
2
+ * Enterprise Audit Logging System for TokNxr
3
+ * Compliant audit trails for security monitoring and compliance
4
+ */
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+ import * as crypto from 'node:crypto';
8
+ import chalk from 'chalk';
9
+ // Audit event types for compliance monitoring
10
+ export var AuditEventType;
11
+ (function (AuditEventType) {
12
+ // Authentication & Authorization
13
+ AuditEventType["AUTH_LOGIN"] = "auth.login";
14
+ AuditEventType["AUTH_LOGOUT"] = "auth.logout";
15
+ AuditEventType["AUTH_FAILED"] = "auth.failed";
16
+ AuditEventType["AUTH_PASSWORD_CHANGE"] = "auth.password_change";
17
+ AuditEventType["AUTH_LOCKOUT"] = "auth.lockout";
18
+ AuditEventType["AUTH_SSO_ACCESS"] = "auth.sso_access";
19
+ // Data Access & Modification
20
+ AuditEventType["DATA_ACCESS"] = "data.access";
21
+ AuditEventType["DATA_MODIFY"] = "data.modify";
22
+ AuditEventType["DATA_DELETE"] = "data.delete";
23
+ AuditEventType["DATA_EXPORT"] = "data.export";
24
+ // AI Interaction Monitoring
25
+ AuditEventType["AI_REQUEST"] = "ai.request";
26
+ AuditEventType["AI_RESPONSE"] = "ai.response";
27
+ AuditEventType["AI_QUALITY_ANALYSIS"] = "ai.quality_analysis";
28
+ AuditEventType["AI_SECURITY_ALERT"] = "ai.security_alert";
29
+ AuditEventType["AI_PERFORMANCE_METRIC"] = "ai.performance_metric";
30
+ // System & Configuration Changes
31
+ AuditEventType["CONFIG_CHANGE"] = "config.change";
32
+ AuditEventType["POLICY_UPDATE"] = "policy.update";
33
+ AuditEventType["USER_MANAGEMENT"] = "user.management";
34
+ // Security Events
35
+ AuditEventType["SECURITY_VIOLATION"] = "security.violation";
36
+ AuditEventType["SECURITY_ANOMALY"] = "security.anomaly";
37
+ AuditEventType["INTEGRITY_CHECK"] = "security.integrity_check";
38
+ // Compliance Events
39
+ AuditEventType["COMPLIANCE_REPORT"] = "compliance.report";
40
+ AuditEventType["COMPLIANCE_VIOLATION"] = "compliance.violation";
41
+ AuditEventType["AUDIT_LOG_ACCESS"] = "audit.log_access";
42
+ // Error & System Events
43
+ AuditEventType["ERROR_OCCURRED"] = "error.occurred";
44
+ AuditEventType["SYSTEM_MAINTENANCE"] = "system.maintenance";
45
+ AuditEventType["PERFORMANCE_DEGRADATION"] = "performance.degradation";
46
+ })(AuditEventType || (AuditEventType = {}));
47
+ /**
48
+ * Enterprise Audit Logger with compliance and security features
49
+ */
50
+ export class EnterpriseAuditLogger {
51
+ constructor(config = {}) {
52
+ this.eventBuffer = [];
53
+ this.bufferSize = 10; // Flush every 10 events
54
+ this.config = {
55
+ enabled: true,
56
+ logLevel: 'info',
57
+ retentionDays: 365, // 1 year retention
58
+ maxFileSize: 100, // 100MB per file
59
+ encryptionEnabled: false,
60
+ remoteSync: false,
61
+ complianceFrameworks: ['GDPR', 'SOX', 'HIPAA'],
62
+ alertThresholds: {
63
+ dailyEventLimit: 10000,
64
+ riskLevelThreshold: 'high'
65
+ },
66
+ ...config
67
+ };
68
+ this.logFilePath = path.resolve(process.cwd(), 'audit.log');
69
+ if (this.config.encryptionEnabled) {
70
+ this.initializeEncryption();
71
+ }
72
+ // Initialize log file if it doesn't exist
73
+ this.initializeLogFile();
74
+ // Don't start maintenance immediately - only when audit logging is actively used
75
+ // this.scheduleMaintenance();
76
+ }
77
+ /**
78
+ * Log an audit event
79
+ */
80
+ log(event) {
81
+ if (!this.config.enabled)
82
+ return;
83
+ // Start maintenance when audit logging is actively used
84
+ this.scheduleMaintenance();
85
+ const auditEvent = {
86
+ id: this.generateEventId(),
87
+ timestamp: new Date().toISOString(),
88
+ ...event
89
+ };
90
+ // Check risk level for alerting
91
+ if (this.shouldAlert(auditEvent)) {
92
+ this.emitAlert(auditEvent);
93
+ }
94
+ // Add to buffer
95
+ this.eventBuffer.push(auditEvent);
96
+ // Flush if buffer is full or contains high-risk events
97
+ if (this.eventBuffer.length >= this.bufferSize ||
98
+ auditEvent.riskLevel === 'critical' ||
99
+ auditEvent.riskLevel === 'high') {
100
+ this.flushBuffer();
101
+ }
102
+ }
103
+ /**
104
+ * Quick logging methods for common events
105
+ */
106
+ logAuthEvent(type, userId, success, details = {}) {
107
+ this.log({
108
+ eventType: type,
109
+ userId,
110
+ action: type.split('.')[1],
111
+ resource: 'authentication',
112
+ result: success ? 'success' : 'failure',
113
+ riskLevel: success ? 'low' : 'medium',
114
+ complianceTags: ['authentication', 'access_control'],
115
+ details: {
116
+ method: 'toknxr_cli',
117
+ ...details
118
+ },
119
+ metadata: {
120
+ version: '1.0.0',
121
+ environment: process.env.NODE_ENV || 'development',
122
+ component: 'cli'
123
+ }
124
+ });
125
+ }
126
+ logAIEvent(type, userId, model, tokens, cost, quality) {
127
+ this.log({
128
+ eventType: type,
129
+ userId,
130
+ action: type.split('.')[1],
131
+ resource: 'ai_interaction',
132
+ resourceId: model,
133
+ result: 'success',
134
+ riskLevel: cost > 1.0 ? 'medium' : 'low', // Higher cost = higher risk
135
+ complianceTags: ['ai_usage', 'cost_tracking'],
136
+ details: {
137
+ model,
138
+ tokens,
139
+ cost_usd: cost,
140
+ quality_score: quality
141
+ },
142
+ metadata: {
143
+ version: '1.0.0',
144
+ environment: process.env.NODE_ENV || 'development',
145
+ component: 'ai_proxy'
146
+ }
147
+ });
148
+ }
149
+ logSecurityEvent(type, severity, details) {
150
+ this.log({
151
+ eventType: type,
152
+ action: 'security_monitoring',
153
+ resource: 'system_security',
154
+ result: 'warning',
155
+ riskLevel: severity,
156
+ complianceTags: ['security', 'monitoring'],
157
+ details,
158
+ metadata: {
159
+ version: '1.0.0',
160
+ environment: process.env.NODE_ENV || 'development',
161
+ component: 'security_monitor'
162
+ }
163
+ });
164
+ }
165
+ /**
166
+ * Query audit events with filtering
167
+ */
168
+ query(options = {}) {
169
+ const events = this.loadAuditEvents();
170
+ return events.filter(event => {
171
+ if (options.eventType && event.eventType !== options.eventType)
172
+ return false;
173
+ if (options.userId && event.userId !== options.userId)
174
+ return false;
175
+ if (options.riskLevel && event.riskLevel !== options.riskLevel)
176
+ return false;
177
+ if (options.dateFrom && event.timestamp < options.dateFrom)
178
+ return false;
179
+ if (options.dateTo && event.timestamp > options.dateTo)
180
+ return false;
181
+ return true;
182
+ }).slice(0, options.limit || 1000);
183
+ }
184
+ /**
185
+ * Generate compliance report for specified period
186
+ */
187
+ generateComplianceReport(startDate, endDate) {
188
+ const events = this.query({
189
+ dateFrom: startDate,
190
+ dateTo: endDate
191
+ });
192
+ const eventsByType = {};
193
+ const eventsByRiskLevel = {};
194
+ const complianceViolations = [];
195
+ events.forEach(event => {
196
+ // Count by type
197
+ eventsByType[event.eventType] = (eventsByType[event.eventType] || 0) + 1;
198
+ // Count by risk level
199
+ eventsByRiskLevel[event.riskLevel] = (eventsByRiskLevel[event.riskLevel] || 0) + 1;
200
+ // Check for compliance violations
201
+ if (this.isComplianceViolation(event)) {
202
+ complianceViolations.push(event);
203
+ }
204
+ });
205
+ const recommendations = this.generateRecommendations(events, complianceViolations);
206
+ return {
207
+ period: { start: startDate, end: endDate },
208
+ totalEvents: events.length,
209
+ eventsByType,
210
+ eventsByRiskLevel,
211
+ complianceViolations,
212
+ riskSummary: {
213
+ critical: eventsByRiskLevel.critical || 0,
214
+ high: eventsByRiskLevel.high || 0,
215
+ medium: eventsByRiskLevel.medium || 0,
216
+ low: eventsByRiskLevel.low || 0
217
+ },
218
+ recommendations
219
+ };
220
+ }
221
+ /**
222
+ * Export audit logs in various formats
223
+ */
224
+ exportAuditData(format, options = {}) {
225
+ const events = this.query(options);
226
+ switch (format) {
227
+ case 'json':
228
+ return JSON.stringify(events, null, 2);
229
+ case 'csv':
230
+ return this.eventsToCSV(events);
231
+ case 'xml':
232
+ return this.eventsToXML(events);
233
+ default:
234
+ throw new Error(`Unsupported export format: ${format}`);
235
+ }
236
+ }
237
+ /**
238
+ * Private methods
239
+ */
240
+ generateEventId() {
241
+ return crypto.randomUUID();
242
+ }
243
+ shouldAlert(event) {
244
+ return event.riskLevel === this.config.alertThresholds.riskLevelThreshold ||
245
+ event.riskLevel === 'critical';
246
+ }
247
+ emitAlert(event) {
248
+ console.warn(chalk.red.bold('🚨 AUDIT ALERT:'), chalk.yellow(event.eventType));
249
+ console.warn(chalk.gray(`Risk Level: ${event.riskLevel.toUpperCase()}`));
250
+ console.warn(chalk.gray(`Action: ${event.action}`));
251
+ console.warn(chalk.gray(`Resource: ${event.resource}`));
252
+ console.warn(chalk.gray(`Time: ${event.timestamp}`));
253
+ // In a real enterprise system, this would send alerts to:
254
+ // - SIEM systems
255
+ // - Email notifications
256
+ // - Incident response teams
257
+ // - Compliance dashboards
258
+ }
259
+ flushBuffer() {
260
+ if (this.eventBuffer.length === 0)
261
+ return;
262
+ try {
263
+ const eventsToWrite = [...this.eventBuffer];
264
+ this.eventBuffer = [];
265
+ const logEntries = eventsToWrite.map(event => {
266
+ const entry = JSON.stringify(event);
267
+ if (this.config.encryptionEnabled && this.encryptionKey) {
268
+ return this.encrypt(entry);
269
+ }
270
+ return entry;
271
+ }).join('\n') + '\n';
272
+ // Check file size before writing
273
+ this.checkFileSize();
274
+ fs.appendFileSync(this.logFilePath, logEntries);
275
+ }
276
+ catch (error) {
277
+ console.error('Failed to write audit logs:', error);
278
+ // Re-add failed events back to buffer
279
+ this.eventBuffer.unshift(...this.eventBuffer);
280
+ }
281
+ }
282
+ loadAuditEvents() {
283
+ try {
284
+ if (!fs.existsSync(this.logFilePath))
285
+ return [];
286
+ const content = fs.readFileSync(this.logFilePath, 'utf8');
287
+ const lines = content.trim().split('\n');
288
+ return lines.map(line => {
289
+ try {
290
+ let parsed;
291
+ if (this.config.encryptionEnabled && this.encryptionKey) {
292
+ parsed = this.decrypt(line);
293
+ }
294
+ else {
295
+ parsed = line;
296
+ }
297
+ return JSON.parse(parsed);
298
+ }
299
+ catch {
300
+ // Skip invalid lines but don't break the entire log
301
+ return null;
302
+ }
303
+ }).filter((event) => event !== null);
304
+ }
305
+ catch (error) {
306
+ console.error('Failed to load audit events:', error);
307
+ return [];
308
+ }
309
+ }
310
+ initializeLogFile() {
311
+ if (!fs.existsSync(this.logFilePath)) {
312
+ fs.writeFileSync(this.logFilePath, '');
313
+ }
314
+ }
315
+ checkFileSize() {
316
+ try {
317
+ const stats = fs.statSync(this.logFilePath);
318
+ const fileSizeMB = stats.size / (1024 * 1024);
319
+ if (fileSizeMB > this.config.maxFileSize) {
320
+ this.rotateLogFile();
321
+ }
322
+ }
323
+ catch {
324
+ // File might not exist, that's fine
325
+ }
326
+ }
327
+ rotateLogFile() {
328
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
329
+ const rotatedPath = `${this.logFilePath}.${timestamp}.rotated`;
330
+ try {
331
+ fs.renameSync(this.logFilePath, rotatedPath);
332
+ this.initializeLogFile();
333
+ // Log rotation event
334
+ this.log({
335
+ eventType: AuditEventType.SYSTEM_MAINTENANCE,
336
+ action: 'log_rotation',
337
+ resource: 'audit_system',
338
+ result: 'success',
339
+ riskLevel: 'low',
340
+ complianceTags: ['system_maintenance'],
341
+ details: { rotatedFile: rotatedPath },
342
+ metadata: {
343
+ version: '1.0.0',
344
+ environment: process.env.NODE_ENV || 'development',
345
+ component: 'audit_logger'
346
+ }
347
+ });
348
+ }
349
+ catch (error) {
350
+ console.error('Failed to rotate audit log:', error);
351
+ }
352
+ }
353
+ scheduleMaintenance() {
354
+ // Only start maintenance if not already running
355
+ if (this.maintenanceInterval)
356
+ return;
357
+ // Run maintenance every hour
358
+ this.maintenanceInterval = setInterval(() => {
359
+ try {
360
+ this.performMaintenance();
361
+ }
362
+ catch (error) {
363
+ console.error('Audit maintenance failed:', error);
364
+ }
365
+ }, 60 * 60 * 1000); // 1 hour
366
+ }
367
+ /**
368
+ * Stop maintenance interval (useful for CLI commands that don't need persistent logging)
369
+ */
370
+ stopMaintenance() {
371
+ if (this.maintenanceInterval) {
372
+ clearInterval(this.maintenanceInterval);
373
+ this.maintenanceInterval = undefined;
374
+ }
375
+ }
376
+ performMaintenance() {
377
+ // Clean up old rotated files
378
+ const retentionMs = this.config.retentionDays * 24 * 60 * 60 * 1000;
379
+ try {
380
+ const files = fs.readdirSync(path.dirname(this.logFilePath))
381
+ .filter(file => file.startsWith(path.basename(this.logFilePath)) && file.includes('.rotated'));
382
+ files.forEach(file => {
383
+ const filePath = path.join(path.dirname(this.logFilePath), file);
384
+ const stats = fs.statSync(filePath);
385
+ if (Date.now() - stats.mtime.getTime() > retentionMs) {
386
+ fs.unlinkSync(filePath);
387
+ }
388
+ });
389
+ }
390
+ catch (error) {
391
+ console.error('Failed to perform audit maintenance:', error);
392
+ }
393
+ }
394
+ initializeEncryption() {
395
+ const keyEnv = process.env.AUDIT_ENCRYPTION_KEY;
396
+ if (keyEnv) {
397
+ this.encryptionKey = Buffer.from(keyEnv, 'hex');
398
+ }
399
+ else {
400
+ // Generate a new key (in production, this should be stored securely)
401
+ this.encryptionKey = crypto.randomBytes(32);
402
+ console.warn(chalk.yellow('⚠️ No AUDIT_ENCRYPTION_KEY found. Using generated key (not secure for production)'));
403
+ }
404
+ }
405
+ encrypt(text) {
406
+ if (!this.encryptionKey)
407
+ return text;
408
+ const iv = crypto.randomBytes(16);
409
+ const cipher = crypto.createCipheriv('aes-256-cbc', this.encryptionKey, iv);
410
+ let encrypted = cipher.update(text, 'utf8', 'hex');
411
+ encrypted += cipher.final('hex');
412
+ return iv.toString('hex') + ':' + encrypted;
413
+ }
414
+ decrypt(encryptedText) {
415
+ if (!this.encryptionKey)
416
+ return encryptedText;
417
+ const [ivHex, encrypted] = encryptedText.split(':');
418
+ const iv = Buffer.from(ivHex, 'hex');
419
+ const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, iv);
420
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
421
+ decrypted += decipher.final('utf8');
422
+ return decrypted;
423
+ }
424
+ isComplianceViolation(event) {
425
+ // Check for various compliance rule violations
426
+ const violations = [];
427
+ // GDPR: Data access without consent
428
+ if (event.eventType === AuditEventType.DATA_ACCESS &&
429
+ !event.details.consentObtained) {
430
+ violations.push('GDPR: Missing data consent');
431
+ }
432
+ // SOX: Unauthorized configuration changes
433
+ if (event.eventType === AuditEventType.CONFIG_CHANGE &&
434
+ event.result === 'failure') {
435
+ violations.push('SOX: Unauthorized config change');
436
+ }
437
+ // HIPAA: Access to sensitive health data
438
+ if (event.details.sensitiveDataType === 'health' &&
439
+ !event.details.accessAuthorized) {
440
+ violations.push('HIPAA: Unauthorized health data access');
441
+ }
442
+ return violations.length > 0;
443
+ }
444
+ generateRecommendations(events, violations) {
445
+ const recommendations = [];
446
+ const highRiskCount = events.filter(e => e.riskLevel === 'high' || e.riskLevel === 'critical').length;
447
+ if (highRiskCount > events.length * 0.1) {
448
+ recommendations.push('Implement additional access controls for high-risk operations');
449
+ }
450
+ if (violations.length > 0) {
451
+ recommendations.push('Review and address compliance violations in audit logs');
452
+ }
453
+ const authFailures = events.filter(e => e.eventType.includes('auth') && e.result === 'failure').length;
454
+ if (authFailures > events.length * 0.05) {
455
+ recommendations.push('Strengthen authentication controls and monitor for brute force attempts');
456
+ }
457
+ return recommendations;
458
+ }
459
+ eventsToCSV(events) {
460
+ if (events.length === 0)
461
+ return '';
462
+ const headers = Object.keys(events[0]).join(',');
463
+ const rows = events.map(event => Object.values(event).map(value => typeof value === 'object' ? JSON.stringify(value) : String(value)).join(','));
464
+ return [headers, ...rows].join('\n');
465
+ }
466
+ eventsToXML(events) {
467
+ let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<audit-events>\n';
468
+ events.forEach(event => {
469
+ xml += ' <event>\n';
470
+ Object.entries(event).forEach(([key, value]) => {
471
+ const valueStr = typeof value === 'object' ? JSON.stringify(value) : String(value);
472
+ xml += ` <${key}>${this.escapeXml(valueStr)}</${key}>\n`;
473
+ });
474
+ xml += ' </event>\n';
475
+ });
476
+ xml += '</audit-events>';
477
+ return xml;
478
+ }
479
+ escapeXml(unsafe) {
480
+ return unsafe
481
+ .replace(/&/g, '&')
482
+ .replace(/</g, '<')
483
+ .replace(/>/g, '>')
484
+ .replace(/"/g, '"')
485
+ .replace(/'/g, '&#39;');
486
+ }
487
+ }
488
+ // Export singleton instance for global use
489
+ export const auditLogger = new EnterpriseAuditLogger();
490
+ // Helper function to enable audit logging globally
491
+ export function initializeAuditLogging(config) {
492
+ // Re-initialize with new config if provided
493
+ if (config) {
494
+ Object.assign(auditLogger, config);
495
+ }
496
+ console.log(chalk.blue('📋 Enterprise audit logging initialized'));
497
+ console.log(chalk.gray(` Log file: ${auditLogger['logFilePath']}`));
498
+ console.log(chalk.gray(` Encryption: ${auditLogger['config'].encryptionEnabled ? 'enabled' : 'disabled'}`));
499
+ console.log(chalk.gray(` Remote sync: ${auditLogger['config'].remoteSync ? 'enabled' : 'disabled'}`));
500
+ }