@pan-sec/notebooklm-mcp 1.4.0 → 1.7.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/README.md +61 -7
- package/SECURITY.md +40 -6
- package/dist/auth/mcp-auth.d.ts.map +1 -1
- package/dist/auth/mcp-auth.js +3 -6
- package/dist/auth/mcp-auth.js.map +1 -1
- package/dist/compliance/alert-manager.d.ts +120 -0
- package/dist/compliance/alert-manager.d.ts.map +1 -0
- package/dist/compliance/alert-manager.js +420 -0
- package/dist/compliance/alert-manager.js.map +1 -0
- package/dist/compliance/breach-detection.d.ts +134 -0
- package/dist/compliance/breach-detection.d.ts.map +1 -0
- package/dist/compliance/breach-detection.js +456 -0
- package/dist/compliance/breach-detection.js.map +1 -0
- package/dist/compliance/change-log.d.ts +113 -0
- package/dist/compliance/change-log.d.ts.map +1 -0
- package/dist/compliance/change-log.js +275 -0
- package/dist/compliance/change-log.js.map +1 -0
- package/dist/compliance/compliance-logger.d.ts +136 -0
- package/dist/compliance/compliance-logger.d.ts.map +1 -0
- package/dist/compliance/compliance-logger.js +425 -0
- package/dist/compliance/compliance-logger.js.map +1 -0
- package/dist/compliance/compliance-tools.d.ts +18 -0
- package/dist/compliance/compliance-tools.d.ts.map +1 -0
- package/dist/compliance/compliance-tools.js +673 -0
- package/dist/compliance/compliance-tools.js.map +1 -0
- package/dist/compliance/consent-manager.d.ts +130 -0
- package/dist/compliance/consent-manager.d.ts.map +1 -0
- package/dist/compliance/consent-manager.js +386 -0
- package/dist/compliance/consent-manager.js.map +1 -0
- package/dist/compliance/dashboard.d.ts +243 -0
- package/dist/compliance/dashboard.d.ts.map +1 -0
- package/dist/compliance/dashboard.js +519 -0
- package/dist/compliance/dashboard.js.map +1 -0
- package/dist/compliance/data-classification.d.ts +117 -0
- package/dist/compliance/data-classification.d.ts.map +1 -0
- package/dist/compliance/data-classification.js +469 -0
- package/dist/compliance/data-classification.js.map +1 -0
- package/dist/compliance/data-erasure.d.ts +110 -0
- package/dist/compliance/data-erasure.d.ts.map +1 -0
- package/dist/compliance/data-erasure.js +501 -0
- package/dist/compliance/data-erasure.js.map +1 -0
- package/dist/compliance/data-export.d.ts +85 -0
- package/dist/compliance/data-export.d.ts.map +1 -0
- package/dist/compliance/data-export.js +394 -0
- package/dist/compliance/data-export.js.map +1 -0
- package/dist/compliance/data-inventory.d.ts +136 -0
- package/dist/compliance/data-inventory.d.ts.map +1 -0
- package/dist/compliance/data-inventory.js +335 -0
- package/dist/compliance/data-inventory.js.map +1 -0
- package/dist/compliance/dsar-handler.d.ts +123 -0
- package/dist/compliance/dsar-handler.d.ts.map +1 -0
- package/dist/compliance/dsar-handler.js +371 -0
- package/dist/compliance/dsar-handler.js.map +1 -0
- package/dist/compliance/evidence-collector.d.ts +187 -0
- package/dist/compliance/evidence-collector.d.ts.map +1 -0
- package/dist/compliance/evidence-collector.js +656 -0
- package/dist/compliance/evidence-collector.js.map +1 -0
- package/dist/compliance/health-monitor.d.ts +111 -0
- package/dist/compliance/health-monitor.d.ts.map +1 -0
- package/dist/compliance/health-monitor.js +509 -0
- package/dist/compliance/health-monitor.js.map +1 -0
- package/dist/compliance/incident-manager.d.ts +131 -0
- package/dist/compliance/incident-manager.d.ts.map +1 -0
- package/dist/compliance/incident-manager.js +418 -0
- package/dist/compliance/incident-manager.js.map +1 -0
- package/dist/compliance/index.d.ts +32 -0
- package/dist/compliance/index.d.ts.map +1 -0
- package/dist/compliance/index.js +35 -0
- package/dist/compliance/index.js.map +1 -0
- package/dist/compliance/policy-docs.d.ts +108 -0
- package/dist/compliance/policy-docs.d.ts.map +1 -0
- package/dist/compliance/policy-docs.js +464 -0
- package/dist/compliance/policy-docs.js.map +1 -0
- package/dist/compliance/privacy-notice-text.d.ts +58 -0
- package/dist/compliance/privacy-notice-text.d.ts.map +1 -0
- package/dist/compliance/privacy-notice-text.js +161 -0
- package/dist/compliance/privacy-notice-text.js.map +1 -0
- package/dist/compliance/privacy-notice.d.ts +128 -0
- package/dist/compliance/privacy-notice.d.ts.map +1 -0
- package/dist/compliance/privacy-notice.js +250 -0
- package/dist/compliance/privacy-notice.js.map +1 -0
- package/dist/compliance/report-generator.d.ts +168 -0
- package/dist/compliance/report-generator.d.ts.map +1 -0
- package/dist/compliance/report-generator.js +830 -0
- package/dist/compliance/report-generator.js.map +1 -0
- package/dist/compliance/retention-engine.d.ts +130 -0
- package/dist/compliance/retention-engine.d.ts.map +1 -0
- package/dist/compliance/retention-engine.js +510 -0
- package/dist/compliance/retention-engine.js.map +1 -0
- package/dist/compliance/siem-exporter.d.ts +150 -0
- package/dist/compliance/siem-exporter.d.ts.map +1 -0
- package/dist/compliance/siem-exporter.js +509 -0
- package/dist/compliance/siem-exporter.js.map +1 -0
- package/dist/compliance/types.d.ts +601 -0
- package/dist/compliance/types.d.ts.map +1 -0
- package/dist/compliance/types.js +22 -0
- package/dist/compliance/types.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/events/event-emitter.d.ts +45 -0
- package/dist/events/event-emitter.d.ts.map +1 -0
- package/dist/events/event-emitter.js +100 -0
- package/dist/events/event-emitter.js.map +1 -0
- package/dist/events/event-types.d.ts +124 -0
- package/dist/events/event-types.d.ts.map +1 -0
- package/dist/events/event-types.js +18 -0
- package/dist/events/event-types.js.map +1 -0
- package/dist/index.js +59 -2
- package/dist/index.js.map +1 -1
- package/dist/library/notebook-library.d.ts +25 -2
- package/dist/library/notebook-library.d.ts.map +1 -1
- package/dist/library/notebook-library.js +144 -3
- package/dist/library/notebook-library.js.map +1 -1
- package/dist/library/types.d.ts +15 -0
- package/dist/library/types.d.ts.map +1 -1
- package/dist/notebook-creation/audio-manager.d.ts +56 -0
- package/dist/notebook-creation/audio-manager.d.ts.map +1 -0
- package/dist/notebook-creation/audio-manager.js +335 -0
- package/dist/notebook-creation/audio-manager.js.map +1 -0
- package/dist/notebook-creation/discover-creation-flow.d.ts +8 -0
- package/dist/notebook-creation/discover-creation-flow.d.ts.map +1 -0
- package/dist/notebook-creation/discover-creation-flow.js +177 -0
- package/dist/notebook-creation/discover-creation-flow.js.map +1 -0
- package/dist/notebook-creation/discover-quota.d.ts +8 -0
- package/dist/notebook-creation/discover-quota.d.ts.map +1 -0
- package/dist/notebook-creation/discover-quota.js +195 -0
- package/dist/notebook-creation/discover-quota.js.map +1 -0
- package/dist/notebook-creation/discover-source-dialog.d.ts +8 -0
- package/dist/notebook-creation/discover-source-dialog.d.ts.map +1 -0
- package/dist/notebook-creation/discover-source-dialog.js +134 -0
- package/dist/notebook-creation/discover-source-dialog.js.map +1 -0
- package/dist/notebook-creation/discover-sources.d.ts +8 -0
- package/dist/notebook-creation/discover-sources.d.ts.map +1 -0
- package/dist/notebook-creation/discover-sources.js +273 -0
- package/dist/notebook-creation/discover-sources.js.map +1 -0
- package/dist/notebook-creation/discover-text-input.d.ts +7 -0
- package/dist/notebook-creation/discover-text-input.d.ts.map +1 -0
- package/dist/notebook-creation/discover-text-input.js +135 -0
- package/dist/notebook-creation/discover-text-input.js.map +1 -0
- package/dist/notebook-creation/index.d.ts +12 -0
- package/dist/notebook-creation/index.d.ts.map +1 -0
- package/dist/notebook-creation/index.js +12 -0
- package/dist/notebook-creation/index.js.map +1 -0
- package/dist/notebook-creation/notebook-creator.d.ts +95 -0
- package/dist/notebook-creation/notebook-creator.d.ts.map +1 -0
- package/dist/notebook-creation/notebook-creator.js +689 -0
- package/dist/notebook-creation/notebook-creator.js.map +1 -0
- package/dist/notebook-creation/notebook-sync.d.ts +93 -0
- package/dist/notebook-creation/notebook-sync.d.ts.map +1 -0
- package/dist/notebook-creation/notebook-sync.js +370 -0
- package/dist/notebook-creation/notebook-sync.js.map +1 -0
- package/dist/notebook-creation/run-discovery.d.ts +11 -0
- package/dist/notebook-creation/run-discovery.d.ts.map +1 -0
- package/dist/notebook-creation/run-discovery.js +151 -0
- package/dist/notebook-creation/run-discovery.js.map +1 -0
- package/dist/notebook-creation/selector-discovery.d.ts +65 -0
- package/dist/notebook-creation/selector-discovery.d.ts.map +1 -0
- package/dist/notebook-creation/selector-discovery.js +421 -0
- package/dist/notebook-creation/selector-discovery.js.map +1 -0
- package/dist/notebook-creation/selectors.d.ts +150 -0
- package/dist/notebook-creation/selectors.d.ts.map +1 -0
- package/dist/notebook-creation/selectors.js +225 -0
- package/dist/notebook-creation/selectors.js.map +1 -0
- package/dist/notebook-creation/source-manager.d.ts +73 -0
- package/dist/notebook-creation/source-manager.d.ts.map +1 -0
- package/dist/notebook-creation/source-manager.js +486 -0
- package/dist/notebook-creation/source-manager.js.map +1 -0
- package/dist/notebook-creation/test-create.d.ts +8 -0
- package/dist/notebook-creation/test-create.d.ts.map +1 -0
- package/dist/notebook-creation/test-create.js +72 -0
- package/dist/notebook-creation/test-create.js.map +1 -0
- package/dist/notebook-creation/types.d.ts +173 -0
- package/dist/notebook-creation/types.d.ts.map +1 -0
- package/dist/notebook-creation/types.js +5 -0
- package/dist/notebook-creation/types.js.map +1 -0
- package/dist/quota/index.d.ts +8 -0
- package/dist/quota/index.d.ts.map +1 -0
- package/dist/quota/index.js +8 -0
- package/dist/quota/index.js.map +1 -0
- package/dist/quota/quota-manager.d.ts +125 -0
- package/dist/quota/quota-manager.d.ts.map +1 -0
- package/dist/quota/quota-manager.js +330 -0
- package/dist/quota/quota-manager.js.map +1 -0
- package/dist/session/session-manager.d.ts +5 -0
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +6 -0
- package/dist/session/session-manager.js.map +1 -1
- package/dist/session/shared-context-manager.d.ts.map +1 -1
- package/dist/session/shared-context-manager.js +2 -1
- package/dist/session/shared-context-manager.js.map +1 -1
- package/dist/tools/definitions/notebook-management.d.ts.map +1 -1
- package/dist/tools/definitions/notebook-management.js +525 -0
- package/dist/tools/definitions/notebook-management.js.map +1 -1
- package/dist/tools/definitions/system.d.ts.map +1 -1
- package/dist/tools/definitions/system.js +158 -0
- package/dist/tools/definitions/system.js.map +1 -1
- package/dist/tools/handlers.d.ts +225 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +911 -0
- package/dist/tools/handlers.js.map +1 -1
- package/dist/utils/audit-logger.d.ts +21 -1
- package/dist/utils/audit-logger.d.ts.map +1 -1
- package/dist/utils/audit-logger.js +53 -4
- package/dist/utils/audit-logger.js.map +1 -1
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +8 -15
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/file-permissions.d.ts +85 -0
- package/dist/utils/file-permissions.d.ts.map +1 -0
- package/dist/utils/file-permissions.js +180 -0
- package/dist/utils/file-permissions.js.map +1 -0
- package/dist/utils/settings-manager.d.ts.map +1 -1
- package/dist/utils/settings-manager.js +6 -11
- package/dist/utils/settings-manager.js.map +1 -1
- package/dist/webhooks/index.d.ts +8 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +8 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/types.d.ts +57 -0
- package/dist/webhooks/types.d.ts.map +1 -0
- package/dist/webhooks/types.js +5 -0
- package/dist/webhooks/types.js.map +1 -0
- package/dist/webhooks/webhook-dispatcher.d.ts +120 -0
- package/dist/webhooks/webhook-dispatcher.d.ts.map +1 -0
- package/dist/webhooks/webhook-dispatcher.js +519 -0
- package/dist/webhooks/webhook-dispatcher.js.map +1 -0
- package/docs/COMPLIANCE-SPEC.md +1452 -0
- 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, "&")
|
|
767
|
+
.replace(/</g, "<")
|
|
768
|
+
.replace(/>/g, ">")
|
|
769
|
+
.replace(/"/g, """)
|
|
770
|
+
.replace(/'/g, "'");
|
|
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
|