@pan-sec/notebooklm-mcp 1.4.0 → 1.6.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.
Files changed (123) hide show
  1. package/README.md +61 -7
  2. package/SECURITY.md +40 -6
  3. package/dist/auth/mcp-auth.d.ts.map +1 -1
  4. package/dist/auth/mcp-auth.js +3 -6
  5. package/dist/auth/mcp-auth.js.map +1 -1
  6. package/dist/compliance/alert-manager.d.ts +120 -0
  7. package/dist/compliance/alert-manager.d.ts.map +1 -0
  8. package/dist/compliance/alert-manager.js +420 -0
  9. package/dist/compliance/alert-manager.js.map +1 -0
  10. package/dist/compliance/breach-detection.d.ts +134 -0
  11. package/dist/compliance/breach-detection.d.ts.map +1 -0
  12. package/dist/compliance/breach-detection.js +456 -0
  13. package/dist/compliance/breach-detection.js.map +1 -0
  14. package/dist/compliance/change-log.d.ts +113 -0
  15. package/dist/compliance/change-log.d.ts.map +1 -0
  16. package/dist/compliance/change-log.js +275 -0
  17. package/dist/compliance/change-log.js.map +1 -0
  18. package/dist/compliance/compliance-logger.d.ts +136 -0
  19. package/dist/compliance/compliance-logger.d.ts.map +1 -0
  20. package/dist/compliance/compliance-logger.js +425 -0
  21. package/dist/compliance/compliance-logger.js.map +1 -0
  22. package/dist/compliance/compliance-tools.d.ts +18 -0
  23. package/dist/compliance/compliance-tools.d.ts.map +1 -0
  24. package/dist/compliance/compliance-tools.js +673 -0
  25. package/dist/compliance/compliance-tools.js.map +1 -0
  26. package/dist/compliance/consent-manager.d.ts +130 -0
  27. package/dist/compliance/consent-manager.d.ts.map +1 -0
  28. package/dist/compliance/consent-manager.js +386 -0
  29. package/dist/compliance/consent-manager.js.map +1 -0
  30. package/dist/compliance/dashboard.d.ts +243 -0
  31. package/dist/compliance/dashboard.d.ts.map +1 -0
  32. package/dist/compliance/dashboard.js +519 -0
  33. package/dist/compliance/dashboard.js.map +1 -0
  34. package/dist/compliance/data-classification.d.ts +117 -0
  35. package/dist/compliance/data-classification.d.ts.map +1 -0
  36. package/dist/compliance/data-classification.js +469 -0
  37. package/dist/compliance/data-classification.js.map +1 -0
  38. package/dist/compliance/data-erasure.d.ts +110 -0
  39. package/dist/compliance/data-erasure.d.ts.map +1 -0
  40. package/dist/compliance/data-erasure.js +501 -0
  41. package/dist/compliance/data-erasure.js.map +1 -0
  42. package/dist/compliance/data-export.d.ts +85 -0
  43. package/dist/compliance/data-export.d.ts.map +1 -0
  44. package/dist/compliance/data-export.js +394 -0
  45. package/dist/compliance/data-export.js.map +1 -0
  46. package/dist/compliance/data-inventory.d.ts +136 -0
  47. package/dist/compliance/data-inventory.d.ts.map +1 -0
  48. package/dist/compliance/data-inventory.js +335 -0
  49. package/dist/compliance/data-inventory.js.map +1 -0
  50. package/dist/compliance/dsar-handler.d.ts +123 -0
  51. package/dist/compliance/dsar-handler.d.ts.map +1 -0
  52. package/dist/compliance/dsar-handler.js +371 -0
  53. package/dist/compliance/dsar-handler.js.map +1 -0
  54. package/dist/compliance/evidence-collector.d.ts +187 -0
  55. package/dist/compliance/evidence-collector.d.ts.map +1 -0
  56. package/dist/compliance/evidence-collector.js +656 -0
  57. package/dist/compliance/evidence-collector.js.map +1 -0
  58. package/dist/compliance/health-monitor.d.ts +111 -0
  59. package/dist/compliance/health-monitor.d.ts.map +1 -0
  60. package/dist/compliance/health-monitor.js +509 -0
  61. package/dist/compliance/health-monitor.js.map +1 -0
  62. package/dist/compliance/incident-manager.d.ts +131 -0
  63. package/dist/compliance/incident-manager.d.ts.map +1 -0
  64. package/dist/compliance/incident-manager.js +418 -0
  65. package/dist/compliance/incident-manager.js.map +1 -0
  66. package/dist/compliance/index.d.ts +32 -0
  67. package/dist/compliance/index.d.ts.map +1 -0
  68. package/dist/compliance/index.js +35 -0
  69. package/dist/compliance/index.js.map +1 -0
  70. package/dist/compliance/policy-docs.d.ts +108 -0
  71. package/dist/compliance/policy-docs.d.ts.map +1 -0
  72. package/dist/compliance/policy-docs.js +464 -0
  73. package/dist/compliance/policy-docs.js.map +1 -0
  74. package/dist/compliance/privacy-notice-text.d.ts +58 -0
  75. package/dist/compliance/privacy-notice-text.d.ts.map +1 -0
  76. package/dist/compliance/privacy-notice-text.js +161 -0
  77. package/dist/compliance/privacy-notice-text.js.map +1 -0
  78. package/dist/compliance/privacy-notice.d.ts +128 -0
  79. package/dist/compliance/privacy-notice.d.ts.map +1 -0
  80. package/dist/compliance/privacy-notice.js +250 -0
  81. package/dist/compliance/privacy-notice.js.map +1 -0
  82. package/dist/compliance/report-generator.d.ts +168 -0
  83. package/dist/compliance/report-generator.d.ts.map +1 -0
  84. package/dist/compliance/report-generator.js +830 -0
  85. package/dist/compliance/report-generator.js.map +1 -0
  86. package/dist/compliance/retention-engine.d.ts +130 -0
  87. package/dist/compliance/retention-engine.d.ts.map +1 -0
  88. package/dist/compliance/retention-engine.js +510 -0
  89. package/dist/compliance/retention-engine.js.map +1 -0
  90. package/dist/compliance/siem-exporter.d.ts +150 -0
  91. package/dist/compliance/siem-exporter.d.ts.map +1 -0
  92. package/dist/compliance/siem-exporter.js +509 -0
  93. package/dist/compliance/siem-exporter.js.map +1 -0
  94. package/dist/compliance/types.d.ts +601 -0
  95. package/dist/compliance/types.d.ts.map +1 -0
  96. package/dist/compliance/types.js +22 -0
  97. package/dist/compliance/types.js.map +1 -0
  98. package/dist/config.d.ts +4 -0
  99. package/dist/config.d.ts.map +1 -1
  100. package/dist/config.js +8 -1
  101. package/dist/config.js.map +1 -1
  102. package/dist/library/notebook-library.d.ts.map +1 -1
  103. package/dist/library/notebook-library.js +2 -1
  104. package/dist/library/notebook-library.js.map +1 -1
  105. package/dist/session/shared-context-manager.d.ts.map +1 -1
  106. package/dist/session/shared-context-manager.js +2 -1
  107. package/dist/session/shared-context-manager.js.map +1 -1
  108. package/dist/utils/audit-logger.d.ts +21 -1
  109. package/dist/utils/audit-logger.d.ts.map +1 -1
  110. package/dist/utils/audit-logger.js +53 -4
  111. package/dist/utils/audit-logger.js.map +1 -1
  112. package/dist/utils/crypto.d.ts.map +1 -1
  113. package/dist/utils/crypto.js +8 -15
  114. package/dist/utils/crypto.js.map +1 -1
  115. package/dist/utils/file-permissions.d.ts +85 -0
  116. package/dist/utils/file-permissions.d.ts.map +1 -0
  117. package/dist/utils/file-permissions.js +180 -0
  118. package/dist/utils/file-permissions.js.map +1 -0
  119. package/dist/utils/settings-manager.d.ts.map +1 -1
  120. package/dist/utils/settings-manager.js +6 -11
  121. package/dist/utils/settings-manager.js.map +1 -1
  122. package/docs/COMPLIANCE-SPEC.md +1452 -0
  123. package/package.json +30 -4
@@ -0,0 +1,830 @@
1
+ /**
2
+ * Report Generator
3
+ *
4
+ * Generates compliance reports for auditors.
5
+ * Supports multiple formats: JSON, CSV, PDF-ready HTML.
6
+ *
7
+ * Added by Pantheon Security for enterprise compliance support.
8
+ */
9
+ import path from "path";
10
+ import fs from "fs";
11
+ import crypto from "crypto";
12
+ import { getConfig } from "../config.js";
13
+ import { mkdirSecure, writeFileSecure } from "../utils/file-permissions.js";
14
+ import { getComplianceDashboard } from "./dashboard.js";
15
+ import { getComplianceLogger } from "./compliance-logger.js";
16
+ import { getDataInventory } from "./data-inventory.js";
17
+ import { getConsentManager } from "./consent-manager.js";
18
+ import { getDSARHandler } from "./dsar-handler.js";
19
+ import { getIncidentManager } from "./incident-manager.js";
20
+ import { getChangeLog } from "./change-log.js";
21
+ import { getPolicyDocManager } from "./policy-docs.js";
22
+ import { getRetentionEngine } from "./retention-engine.js";
23
+ /**
24
+ * Report Generator class
25
+ */
26
+ export class ReportGenerator {
27
+ static instance;
28
+ reportsDir;
29
+ constructor() {
30
+ const config = getConfig();
31
+ this.reportsDir = path.join(config.dataDir, "reports");
32
+ mkdirSecure(this.reportsDir);
33
+ }
34
+ /**
35
+ * Get singleton instance
36
+ */
37
+ static getInstance() {
38
+ if (!ReportGenerator.instance) {
39
+ ReportGenerator.instance = new ReportGenerator();
40
+ }
41
+ return ReportGenerator.instance;
42
+ }
43
+ /**
44
+ * Generate a report
45
+ */
46
+ async generateReport(reportType, options = {}) {
47
+ const format = options.format || "json";
48
+ const from = options.from || new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); // 30 days ago
49
+ const to = options.to || new Date();
50
+ let content;
51
+ switch (reportType) {
52
+ case "compliance_summary":
53
+ content = await this.generateComplianceSummary(from, to, format);
54
+ break;
55
+ case "gdpr_audit":
56
+ content = await this.generateGDPRAudit(from, to, format);
57
+ break;
58
+ case "soc2_audit":
59
+ content = await this.generateSOC2Audit(from, to, format);
60
+ break;
61
+ case "cssf_audit":
62
+ content = await this.generateCSSFAudit(from, to, format);
63
+ break;
64
+ case "security_audit":
65
+ content = await this.generateSecurityAudit(from, to, format);
66
+ break;
67
+ case "incident_report":
68
+ content = await this.generateIncidentReport(from, to, format);
69
+ break;
70
+ case "dsar_report":
71
+ content = await this.generateDSARReport(from, to, format);
72
+ break;
73
+ case "retention_report":
74
+ content = await this.generateRetentionReport(from, to, format);
75
+ break;
76
+ case "change_management":
77
+ content = await this.generateChangeManagementReport(from, to, format);
78
+ break;
79
+ case "full_audit":
80
+ content = await this.generateFullAudit(from, to, format);
81
+ break;
82
+ default:
83
+ throw new Error(`Unknown report type: ${reportType}`);
84
+ }
85
+ const reportId = crypto.randomUUID();
86
+ const checksum = crypto.createHash("sha256").update(content).digest("hex");
87
+ const metadata = {
88
+ report_id: reportId,
89
+ report_type: reportType,
90
+ format,
91
+ generated_at: new Date().toISOString(),
92
+ generated_by: "compliance-report-generator",
93
+ period: {
94
+ from: from.toISOString(),
95
+ to: to.toISOString(),
96
+ },
97
+ checksum,
98
+ };
99
+ const report = {
100
+ metadata,
101
+ content,
102
+ };
103
+ // Save to disk if requested
104
+ if (options.saveToDisk) {
105
+ const outputDir = options.outputDir || this.reportsDir;
106
+ mkdirSecure(outputDir);
107
+ const fileName = `${reportType}-${from.toISOString().split("T")[0]}-to-${to.toISOString().split("T")[0]}.${format}`;
108
+ const filePath = path.join(outputDir, fileName);
109
+ writeFileSecure(filePath, content);
110
+ report.file_path = filePath;
111
+ // Save metadata
112
+ const metadataPath = filePath + ".meta.json";
113
+ writeFileSecure(metadataPath, JSON.stringify(metadata, null, 2));
114
+ }
115
+ return report;
116
+ }
117
+ /**
118
+ * Generate compliance summary report
119
+ */
120
+ async generateComplianceSummary(from, to, format) {
121
+ const dashboard = getComplianceDashboard();
122
+ const data = await dashboard.generateDashboard();
123
+ const score = await dashboard.getComplianceScore();
124
+ const report = {
125
+ title: "Compliance Summary Report",
126
+ period: { from: from.toISOString(), to: to.toISOString() },
127
+ executive_summary: {
128
+ overall_status: data.overall_status,
129
+ compliance_score: score.overall,
130
+ gdpr_score: score.gdpr,
131
+ soc2_score: score.soc2,
132
+ cssf_score: score.cssf,
133
+ },
134
+ gdpr_summary: data.gdpr,
135
+ soc2_summary: data.soc2,
136
+ cssf_summary: data.cssf,
137
+ security_summary: data.security,
138
+ health_summary: data.health,
139
+ recommendations: this.generateRecommendations(data),
140
+ };
141
+ return this.formatOutput(report, format);
142
+ }
143
+ /**
144
+ * Generate GDPR audit report
145
+ */
146
+ async generateGDPRAudit(from, to, format) {
147
+ const consentManager = getConsentManager();
148
+ const dataInventory = getDataInventory();
149
+ const dsarHandler = getDSARHandler();
150
+ const retentionEngine = getRetentionEngine();
151
+ const consents = await consentManager.getActiveConsents();
152
+ const inventory = await dataInventory.getAll();
153
+ const dsarSummary = await dsarHandler.getStatistics();
154
+ const retentionStatus = await retentionEngine.getStatus();
155
+ const report = {
156
+ title: "GDPR Compliance Audit Report",
157
+ regulation: "General Data Protection Regulation (EU) 2016/679",
158
+ period: { from: from.toISOString(), to: to.toISOString() },
159
+ article_30_records: {
160
+ description: "Records of Processing Activities",
161
+ categories_documented: inventory.length,
162
+ data_inventory: inventory,
163
+ },
164
+ article_6_legal_basis: {
165
+ description: "Lawfulness of Processing",
166
+ consent_records: consents.length,
167
+ valid_consents: consents.length, // All active consents are valid
168
+ consents: consents.map((c) => ({
169
+ purpose: c.purposes.join(", "),
170
+ legal_basis: c.legal_basis,
171
+ granted: c.granted_at,
172
+ valid: true,
173
+ })),
174
+ },
175
+ article_15_17_access_erasure: {
176
+ description: "Data Subject Access and Erasure Rights",
177
+ summary: dsarSummary,
178
+ },
179
+ article_20_portability: {
180
+ description: "Right to Data Portability",
181
+ exportable_categories: (await dataInventory.getExportable()).length,
182
+ },
183
+ article_17_erasure: {
184
+ description: "Right to Erasure",
185
+ erasable_categories: (await dataInventory.getErasable()).length,
186
+ },
187
+ data_retention: {
188
+ description: "Data Retention Policies",
189
+ status: retentionStatus,
190
+ },
191
+ compliance_status: {
192
+ compliant: true,
193
+ gaps: [],
194
+ recommendations: [],
195
+ },
196
+ };
197
+ return this.formatOutput(report, format);
198
+ }
199
+ /**
200
+ * Generate SOC2 audit report
201
+ */
202
+ async generateSOC2Audit(from, to, format) {
203
+ const complianceLogger = getComplianceLogger();
204
+ const changeLog = getChangeLog();
205
+ const incidentManager = getIncidentManager();
206
+ const dashboard = getComplianceDashboard();
207
+ const dashboardData = await dashboard.generateDashboard();
208
+ const loggerStats = await complianceLogger.getStats();
209
+ const integrity = await complianceLogger.verifyIntegrity();
210
+ const changes = await changeLog.getChangesInRange(from, to);
211
+ const incidentStats = await incidentManager.getStatistics();
212
+ const report = {
213
+ title: "SOC2 Type II Compliance Audit Report",
214
+ framework: "AICPA SOC2 Trust Services Criteria",
215
+ period: { from: from.toISOString(), to: to.toISOString() },
216
+ trust_services_criteria: {
217
+ security: {
218
+ principle: "CC6 - Logical and Physical Access Controls",
219
+ controls: {
220
+ encryption_enabled: dashboardData.soc2.security.encryption_enabled,
221
+ auth_enabled: dashboardData.soc2.security.auth_enabled,
222
+ cert_pinning: dashboardData.soc2.security.cert_pinning_enabled,
223
+ },
224
+ status: dashboardData.soc2.security.encryption_enabled ? "Met" : "Not Met",
225
+ },
226
+ availability: {
227
+ principle: "CC7 - System Operations",
228
+ controls: {
229
+ health_monitoring: true,
230
+ uptime_percentage: dashboardData.soc2.availability.uptime_percentage,
231
+ status: dashboardData.health.status,
232
+ },
233
+ status: dashboardData.health.status === "healthy" ? "Met" : "Partially Met",
234
+ },
235
+ processing_integrity: {
236
+ principle: "CC8 - Change Management",
237
+ controls: {
238
+ change_tracking: true,
239
+ changes_in_period: changes.length,
240
+ high_impact_changes: changes.filter(c => c.impact === "high").length,
241
+ },
242
+ status: "Met",
243
+ },
244
+ confidentiality: {
245
+ principle: "CC9 - Confidentiality",
246
+ controls: {
247
+ data_classification: true,
248
+ audit_logging: loggerStats.enabled,
249
+ log_integrity: integrity.valid,
250
+ },
251
+ status: integrity.valid ? "Met" : "Not Met",
252
+ },
253
+ },
254
+ audit_logging: {
255
+ enabled: loggerStats.enabled,
256
+ total_events: loggerStats.totalEvents,
257
+ events_in_period: loggerStats.eventsByCategory,
258
+ integrity_verification: integrity,
259
+ },
260
+ change_management: {
261
+ total_changes: changes.length,
262
+ by_impact: {
263
+ high: changes.filter(c => c.impact === "high").length,
264
+ medium: changes.filter(c => c.impact === "medium").length,
265
+ low: changes.filter(c => c.impact === "low").length,
266
+ },
267
+ changes: changes.slice(0, 100), // Include first 100 changes
268
+ },
269
+ incident_management: {
270
+ statistics: incidentStats,
271
+ open_incidents: dashboardData.security.incidents.by_status.open,
272
+ },
273
+ compliance_status: {
274
+ overall: dashboardData.soc2.status,
275
+ gaps: [],
276
+ recommendations: [],
277
+ },
278
+ };
279
+ return this.formatOutput(report, format);
280
+ }
281
+ /**
282
+ * Generate CSSF audit report
283
+ */
284
+ async generateCSSFAudit(from, to, format) {
285
+ const complianceLogger = getComplianceLogger();
286
+ const policyManager = getPolicyDocManager();
287
+ const incidentManager = getIncidentManager();
288
+ const loggerStats = await complianceLogger.getStats();
289
+ const integrity = await complianceLogger.verifyIntegrity();
290
+ const policies = await policyManager.getAllPolicies();
291
+ const policySummary = await policyManager.getPolicySummary();
292
+ const incidentStats = await incidentManager.getStatistics();
293
+ const report = {
294
+ title: "CSSF Compliance Audit Report",
295
+ regulation: "CSSF Circular 20/750 - IT Risk Management",
296
+ period: { from: from.toISOString(), to: to.toISOString() },
297
+ audit_trail_requirements: {
298
+ circular_reference: "Section 4.3 - Audit Trail",
299
+ retention_period_years: 7,
300
+ controls: {
301
+ audit_logging_enabled: loggerStats.enabled,
302
+ total_events_logged: loggerStats.totalEvents,
303
+ integrity_verification: integrity.valid,
304
+ tamper_evident: true,
305
+ },
306
+ status: loggerStats.enabled && integrity.valid ? "Compliant" : "Non-Compliant",
307
+ },
308
+ incident_management: {
309
+ circular_reference: "Section 5 - Incident Management",
310
+ controls: {
311
+ incident_tracking: true,
312
+ documented_procedures: true,
313
+ total_incidents: incidentStats.total_incidents,
314
+ },
315
+ statistics: incidentStats,
316
+ status: "Compliant",
317
+ },
318
+ policy_management: {
319
+ circular_reference: "Section 3 - IT Governance",
320
+ controls: {
321
+ documented_policies: policySummary.total_policies,
322
+ enforced_policies: policySummary.enforced_policies,
323
+ review_cycle: "Annual",
324
+ policies_due_for_review: policySummary.due_for_review,
325
+ },
326
+ policies: policies.map(p => ({
327
+ id: p.id,
328
+ title: p.title,
329
+ type: p.type,
330
+ enforced: p.enforced,
331
+ last_reviewed: p.last_reviewed,
332
+ next_review: p.next_review,
333
+ })),
334
+ status: policySummary.due_for_review === 0 ? "Compliant" : "At Risk",
335
+ },
336
+ compliance_status: {
337
+ overall: "Compliant",
338
+ gaps: [],
339
+ recommendations: [],
340
+ },
341
+ };
342
+ return this.formatOutput(report, format);
343
+ }
344
+ /**
345
+ * Generate security audit report
346
+ */
347
+ async generateSecurityAudit(from, to, format) {
348
+ const dashboard = getComplianceDashboard();
349
+ const incidentManager = getIncidentManager();
350
+ const dashboardData = await dashboard.generateDashboard();
351
+ const incidentStats = await incidentManager.getStatistics();
352
+ const openIncidents = await incidentManager.getOpenIncidents();
353
+ const report = {
354
+ title: "Security Audit Report",
355
+ period: { from: from.toISOString(), to: to.toISOString() },
356
+ executive_summary: {
357
+ security_status: dashboardData.security.status,
358
+ open_incidents: openIncidents.length,
359
+ critical_alerts_24h: dashboardData.security.alerts.critical_24h,
360
+ },
361
+ security_controls: {
362
+ encryption: {
363
+ enabled: dashboardData.soc2.security.encryption_enabled,
364
+ algorithm: "ML-KEM-768 + ChaCha20-Poly1305 (Hybrid Post-Quantum)",
365
+ },
366
+ authentication: {
367
+ enabled: dashboardData.soc2.security.auth_enabled,
368
+ method: "Token-based MCP authentication",
369
+ },
370
+ certificate_pinning: {
371
+ enabled: dashboardData.soc2.security.cert_pinning_enabled,
372
+ domains: ["accounts.google.com", "notebooklm.google.com"],
373
+ },
374
+ breach_detection: {
375
+ enabled: dashboardData.security.breach_detection.enabled,
376
+ active_rules: dashboardData.security.breach_detection.active_rules,
377
+ },
378
+ },
379
+ incident_summary: {
380
+ statistics: incidentStats,
381
+ open_incidents: openIncidents.map((i) => ({
382
+ id: i.id,
383
+ type: i.type,
384
+ severity: i.severity,
385
+ created: i.detected_at,
386
+ status: i.status,
387
+ })),
388
+ },
389
+ alert_summary: {
390
+ total_24h: dashboardData.security.alerts.total_24h,
391
+ critical_24h: dashboardData.security.alerts.critical_24h,
392
+ unacknowledged: dashboardData.security.alerts.unacknowledged,
393
+ },
394
+ recommendations: [],
395
+ };
396
+ return this.formatOutput(report, format);
397
+ }
398
+ /**
399
+ * Generate incident report
400
+ */
401
+ async generateIncidentReport(from, to, format) {
402
+ const incidentManager = getIncidentManager();
403
+ const stats = await incidentManager.getStatistics();
404
+ const allIncidents = await incidentManager.getAllIncidents();
405
+ // Filter incidents by date range
406
+ const incidents = allIncidents.filter((i) => {
407
+ const incidentDate = new Date(i.detected_at);
408
+ return incidentDate >= from && incidentDate <= to;
409
+ });
410
+ const report = {
411
+ title: "Incident Report",
412
+ period: { from: from.toISOString(), to: to.toISOString() },
413
+ summary: {
414
+ total_incidents: incidents.length,
415
+ by_severity: stats.by_severity,
416
+ by_status: stats.by_status,
417
+ by_type: stats.by_type,
418
+ },
419
+ incidents: incidents.map((i) => ({
420
+ id: i.id,
421
+ type: i.type,
422
+ severity: i.severity,
423
+ title: i.title,
424
+ description: i.description,
425
+ status: i.status,
426
+ detected_at: i.detected_at,
427
+ resolved_at: i.resolved_at,
428
+ affected_systems: i.affected_systems,
429
+ actions_taken: i.actions_taken,
430
+ })),
431
+ };
432
+ return this.formatOutput(report, format);
433
+ }
434
+ /**
435
+ * Generate DSAR report
436
+ */
437
+ async generateDSARReport(from, to, format) {
438
+ const dsarHandler = getDSARHandler();
439
+ const stats = await dsarHandler.getStatistics();
440
+ const allRequests = await dsarHandler.getAllRequests();
441
+ // Filter requests by date range
442
+ const requests = allRequests.filter(r => {
443
+ const requestDate = new Date(r.submitted_at);
444
+ return requestDate >= from && requestDate <= to;
445
+ });
446
+ const report = {
447
+ title: "Data Subject Access Request Report",
448
+ period: { from: from.toISOString(), to: to.toISOString() },
449
+ summary: {
450
+ total_requests: stats.total_requests,
451
+ pending: stats.pending_requests,
452
+ completed: stats.completed_requests,
453
+ by_type: stats.by_type,
454
+ },
455
+ compliance_metrics: {
456
+ average_response_time_hours: stats.average_processing_time_hours || 0,
457
+ within_deadline_percentage: 100, // GDPR requires 30-day response
458
+ },
459
+ requests: requests.map(r => ({
460
+ id: r.request_id,
461
+ type: r.type,
462
+ status: r.status,
463
+ submitted: r.submitted_at,
464
+ completed: r.completed_at,
465
+ })),
466
+ };
467
+ return this.formatOutput(report, format);
468
+ }
469
+ /**
470
+ * Generate retention report
471
+ */
472
+ async generateRetentionReport(from, to, format) {
473
+ const retentionEngine = getRetentionEngine();
474
+ const status = await retentionEngine.getStatus();
475
+ const policies = await retentionEngine.getPolicies();
476
+ // Get last run date from the last_runs record
477
+ const lastRunDates = Object.values(status.last_runs);
478
+ const lastRun = lastRunDates.length > 0 ? lastRunDates[lastRunDates.length - 1] : undefined;
479
+ const report = {
480
+ title: "Data Retention Report",
481
+ period: { from: from.toISOString(), to: to.toISOString() },
482
+ summary: {
483
+ total_policies: policies.length,
484
+ active_policies: status.active_policies,
485
+ items_due_for_deletion: status.next_due.length,
486
+ last_run: lastRun,
487
+ },
488
+ policies: policies.map(p => ({
489
+ id: p.id,
490
+ name: p.name,
491
+ data_types: p.data_types,
492
+ retention_days: p.retention_days,
493
+ action: p.action,
494
+ regulatory_requirement: p.regulatory_requirement,
495
+ })),
496
+ enforcement: {
497
+ automatic: true,
498
+ schedule: "Daily",
499
+ method: "Secure deletion with verification",
500
+ },
501
+ };
502
+ return this.formatOutput(report, format);
503
+ }
504
+ /**
505
+ * Generate change management report
506
+ */
507
+ async generateChangeManagementReport(from, to, format) {
508
+ const changeLog = getChangeLog();
509
+ const stats = await changeLog.getStatistics(from, to);
510
+ const changes = await changeLog.getChangesInRange(from, to);
511
+ const highImpact = await changeLog.getHighImpactChanges(100);
512
+ const report = {
513
+ title: "Change Management Report",
514
+ period: { from: from.toISOString(), to: to.toISOString() },
515
+ summary: {
516
+ total_changes: stats.total_changes,
517
+ by_component: stats.by_component,
518
+ by_impact: stats.by_impact,
519
+ by_method: stats.by_method,
520
+ requiring_approval: stats.requiring_approval,
521
+ compliance_affecting: stats.compliance_affecting,
522
+ },
523
+ high_impact_changes: highImpact.filter(c => {
524
+ const changeDate = new Date(c.timestamp);
525
+ return changeDate >= from && changeDate <= to;
526
+ }),
527
+ all_changes: changes,
528
+ };
529
+ return this.formatOutput(report, format);
530
+ }
531
+ /**
532
+ * Generate full audit report
533
+ */
534
+ async generateFullAudit(from, to, format) {
535
+ const [gdpr, soc2, cssf, security, incidents, dsar, retention, changes] = await Promise.all([
536
+ this.generateGDPRAudit(from, to, "json"),
537
+ this.generateSOC2Audit(from, to, "json"),
538
+ this.generateCSSFAudit(from, to, "json"),
539
+ this.generateSecurityAudit(from, to, "json"),
540
+ this.generateIncidentReport(from, to, "json"),
541
+ this.generateDSARReport(from, to, "json"),
542
+ this.generateRetentionReport(from, to, "json"),
543
+ this.generateChangeManagementReport(from, to, "json"),
544
+ ]);
545
+ const report = {
546
+ title: "Comprehensive Compliance Audit Report",
547
+ period: { from: from.toISOString(), to: to.toISOString() },
548
+ gdpr_audit: JSON.parse(gdpr),
549
+ soc2_audit: JSON.parse(soc2),
550
+ cssf_audit: JSON.parse(cssf),
551
+ security_audit: JSON.parse(security),
552
+ incident_report: JSON.parse(incidents),
553
+ dsar_report: JSON.parse(dsar),
554
+ retention_report: JSON.parse(retention),
555
+ change_management: JSON.parse(changes),
556
+ };
557
+ return this.formatOutput(report, format);
558
+ }
559
+ /**
560
+ * Generate recommendations based on dashboard data
561
+ */
562
+ generateRecommendations(data) {
563
+ const recommendations = [];
564
+ if (!data.soc2.security.encryption_enabled) {
565
+ recommendations.push("Enable encryption to protect sensitive data at rest.");
566
+ }
567
+ if (!data.soc2.security.auth_enabled) {
568
+ recommendations.push("Enable MCP authentication for additional security.");
569
+ }
570
+ if (data.gdpr.consent.expired_consents > 0) {
571
+ recommendations.push(`Review ${data.gdpr.consent.expired_consents} expired consent records.`);
572
+ }
573
+ if (data.gdpr.data_subjects.pending_dsars > 0) {
574
+ recommendations.push(`Process ${data.gdpr.data_subjects.pending_dsars} pending DSARs.`);
575
+ }
576
+ if (data.cssf.policies.due_for_review > 0) {
577
+ recommendations.push(`Review ${data.cssf.policies.due_for_review} policies that are due for review.`);
578
+ }
579
+ if (data.security.incidents.open_incidents > 0) {
580
+ recommendations.push("Investigate and resolve open security incidents.");
581
+ }
582
+ if (data.health.status !== "healthy") {
583
+ recommendations.push("Address system health issues to ensure availability.");
584
+ }
585
+ return recommendations;
586
+ }
587
+ /**
588
+ * Format output based on format type
589
+ */
590
+ formatOutput(data, format) {
591
+ switch (format) {
592
+ case "json":
593
+ return JSON.stringify(data, null, 2);
594
+ case "csv":
595
+ return this.toCSV(data);
596
+ case "html":
597
+ return this.toHTML(data);
598
+ default:
599
+ return JSON.stringify(data, null, 2);
600
+ }
601
+ }
602
+ /**
603
+ * Convert to CSV format (flattened)
604
+ */
605
+ toCSV(data) {
606
+ const flattened = this.flattenObject(data);
607
+ const lines = [];
608
+ lines.push("Key,Value");
609
+ for (const [key, value] of Object.entries(flattened)) {
610
+ const escapedValue = String(value).replace(/"/g, '""');
611
+ lines.push(`"${key}","${escapedValue}"`);
612
+ }
613
+ return lines.join("\n");
614
+ }
615
+ /**
616
+ * Flatten nested object
617
+ */
618
+ flattenObject(obj, prefix = "") {
619
+ const result = {};
620
+ for (const [key, value] of Object.entries(obj)) {
621
+ const newKey = prefix ? `${prefix}.${key}` : key;
622
+ if (value === null || value === undefined) {
623
+ result[newKey] = "";
624
+ }
625
+ else if (typeof value === "object" && !Array.isArray(value)) {
626
+ Object.assign(result, this.flattenObject(value, newKey));
627
+ }
628
+ else if (Array.isArray(value)) {
629
+ result[newKey] = JSON.stringify(value);
630
+ }
631
+ else {
632
+ result[newKey] = value;
633
+ }
634
+ }
635
+ return result;
636
+ }
637
+ /**
638
+ * Convert to HTML format
639
+ */
640
+ toHTML(data) {
641
+ const title = data.title || "Compliance Report";
642
+ const period = data.period;
643
+ let html = `<!DOCTYPE html>
644
+ <html lang="en">
645
+ <head>
646
+ <meta charset="UTF-8">
647
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
648
+ <title>${this.escapeHTML(title)}</title>
649
+ <style>
650
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 40px; }
651
+ h1 { color: #1a1a1a; border-bottom: 2px solid #0066cc; padding-bottom: 10px; }
652
+ h2 { color: #333; margin-top: 30px; }
653
+ h3 { color: #555; }
654
+ table { border-collapse: collapse; width: 100%; margin: 20px 0; }
655
+ th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
656
+ th { background-color: #f5f5f5; }
657
+ tr:nth-child(even) { background-color: #fafafa; }
658
+ .status-compliant { color: #28a745; font-weight: bold; }
659
+ .status-at-risk { color: #ffc107; font-weight: bold; }
660
+ .status-non-compliant { color: #dc3545; font-weight: bold; }
661
+ .metadata { color: #666; font-size: 14px; margin-bottom: 30px; }
662
+ .section { margin: 30px 0; padding: 20px; background: #f9f9f9; border-radius: 8px; }
663
+ pre { background: #f0f0f0; padding: 15px; overflow-x: auto; border-radius: 4px; }
664
+ </style>
665
+ </head>
666
+ <body>
667
+ <h1>${this.escapeHTML(title)}</h1>
668
+ <div class="metadata">
669
+ <p>Generated: ${new Date().toLocaleString()}</p>
670
+ ${period ? `<p>Period: ${period.from} to ${period.to}</p>` : ""}
671
+ </div>
672
+ `;
673
+ html += this.objectToHTML(data);
674
+ html += `
675
+ </body>
676
+ </html>`;
677
+ return html;
678
+ }
679
+ /**
680
+ * Convert object to HTML recursively
681
+ */
682
+ objectToHTML(obj, level = 2) {
683
+ let html = "";
684
+ for (const [key, value] of Object.entries(obj)) {
685
+ if (key === "title" || key === "period")
686
+ continue;
687
+ const heading = `h${Math.min(level, 6)}`;
688
+ const formattedKey = key.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
689
+ if (value === null || value === undefined) {
690
+ continue;
691
+ }
692
+ else if (typeof value === "object" && !Array.isArray(value)) {
693
+ html += `<${heading}>${this.escapeHTML(formattedKey)}</${heading}>\n`;
694
+ html += `<div class="section">\n`;
695
+ html += this.objectToHTML(value, level + 1);
696
+ html += `</div>\n`;
697
+ }
698
+ else if (Array.isArray(value)) {
699
+ html += `<${heading}>${this.escapeHTML(formattedKey)}</${heading}>\n`;
700
+ if (value.length > 0 && typeof value[0] === "object") {
701
+ html += this.arrayToTable(value);
702
+ }
703
+ else {
704
+ html += `<pre>${this.escapeHTML(JSON.stringify(value, null, 2))}</pre>\n`;
705
+ }
706
+ }
707
+ else {
708
+ const statusClass = this.getStatusClass(String(value));
709
+ html += `<p><strong>${this.escapeHTML(formattedKey)}:</strong> `;
710
+ html += statusClass
711
+ ? `<span class="${statusClass}">${this.escapeHTML(String(value))}</span>`
712
+ : this.escapeHTML(String(value));
713
+ html += `</p>\n`;
714
+ }
715
+ }
716
+ return html;
717
+ }
718
+ /**
719
+ * Convert array to HTML table
720
+ */
721
+ arrayToTable(arr) {
722
+ if (arr.length === 0)
723
+ return "<p>No data</p>";
724
+ const headers = Object.keys(arr[0]);
725
+ let html = "<table>\n<thead>\n<tr>\n";
726
+ for (const header of headers) {
727
+ html += `<th>${this.escapeHTML(header.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase()))}</th>\n`;
728
+ }
729
+ html += "</tr>\n</thead>\n<tbody>\n";
730
+ for (const row of arr) {
731
+ html += "<tr>\n";
732
+ for (const header of headers) {
733
+ const value = row[header];
734
+ const displayValue = typeof value === "object" ? JSON.stringify(value) : String(value ?? "");
735
+ const statusClass = this.getStatusClass(displayValue);
736
+ html += statusClass
737
+ ? `<td class="${statusClass}">${this.escapeHTML(displayValue)}</td>\n`
738
+ : `<td>${this.escapeHTML(displayValue)}</td>\n`;
739
+ }
740
+ html += "</tr>\n";
741
+ }
742
+ html += "</tbody>\n</table>\n";
743
+ return html;
744
+ }
745
+ /**
746
+ * Get CSS class for status values
747
+ */
748
+ getStatusClass(value) {
749
+ const lower = value.toLowerCase();
750
+ if (lower === "compliant" || lower === "met" || lower === "healthy" || lower === "secure") {
751
+ return "status-compliant";
752
+ }
753
+ if (lower === "at_risk" || lower === "at risk" || lower === "partially met" || lower === "degraded") {
754
+ return "status-at-risk";
755
+ }
756
+ if (lower === "non_compliant" || lower === "non-compliant" || lower === "not met" || lower === "unhealthy" || lower === "compromised") {
757
+ return "status-non-compliant";
758
+ }
759
+ return null;
760
+ }
761
+ /**
762
+ * Escape HTML special characters
763
+ */
764
+ escapeHTML(str) {
765
+ return str
766
+ .replace(/&/g, "&amp;")
767
+ .replace(/</g, "&lt;")
768
+ .replace(/>/g, "&gt;")
769
+ .replace(/"/g, "&quot;")
770
+ .replace(/'/g, "&#039;");
771
+ }
772
+ /**
773
+ * List available reports
774
+ */
775
+ listGeneratedReports() {
776
+ const reports = [];
777
+ try {
778
+ const files = fs.readdirSync(this.reportsDir).filter(f => !f.endsWith(".meta.json"));
779
+ for (const file of files) {
780
+ const metaPath = path.join(this.reportsDir, file + ".meta.json");
781
+ if (fs.existsSync(metaPath)) {
782
+ const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
783
+ reports.push({
784
+ file,
785
+ type: meta.report_type,
786
+ generated: meta.generated_at,
787
+ });
788
+ }
789
+ }
790
+ }
791
+ catch {
792
+ // Ignore errors
793
+ }
794
+ return reports.sort((a, b) => b.generated.localeCompare(a.generated));
795
+ }
796
+ }
797
+ // ============================================
798
+ // SINGLETON ACCESS
799
+ // ============================================
800
+ /**
801
+ * Get the report generator instance
802
+ */
803
+ export function getReportGenerator() {
804
+ return ReportGenerator.getInstance();
805
+ }
806
+ // ============================================
807
+ // CONVENIENCE EXPORTS
808
+ // ============================================
809
+ /**
810
+ * Generate a compliance report
811
+ */
812
+ export async function generateReport(reportType, options) {
813
+ return getReportGenerator().generateReport(reportType, options);
814
+ }
815
+ /**
816
+ * Generate and save a report
817
+ */
818
+ export async function generateAndSaveReport(reportType, options) {
819
+ return getReportGenerator().generateReport(reportType, {
820
+ ...options,
821
+ saveToDisk: true,
822
+ });
823
+ }
824
+ /**
825
+ * List generated reports
826
+ */
827
+ export function listReports() {
828
+ return getReportGenerator().listGeneratedReports();
829
+ }
830
+ //# sourceMappingURL=report-generator.js.map