guardrail-compliance 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/emitter.d.ts +97 -0
- package/dist/audit/emitter.d.ts.map +1 -0
- package/dist/audit/emitter.js +197 -0
- package/dist/audit/events.d.ts +304 -0
- package/dist/audit/events.d.ts.map +1 -0
- package/dist/audit/events.js +267 -0
- package/dist/audit/index.d.ts +11 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +51 -0
- package/dist/audit/storage.d.ts +93 -0
- package/dist/audit/storage.d.ts.map +1 -0
- package/dist/audit/storage.js +337 -0
- package/dist/automation/__tests__/compliance-scheduler.test.d.ts +2 -0
- package/dist/automation/__tests__/compliance-scheduler.test.d.ts.map +1 -0
- package/dist/automation/__tests__/compliance-scheduler.test.js +140 -0
- package/dist/automation/audit-logger.d.ts +129 -0
- package/dist/automation/audit-logger.d.ts.map +1 -0
- package/dist/automation/audit-logger.js +473 -0
- package/dist/automation/compliance-scheduler-fixed.d.ts +1 -0
- package/dist/automation/compliance-scheduler-fixed.d.ts.map +1 -0
- package/dist/automation/compliance-scheduler-fixed.js +1 -0
- package/dist/automation/compliance-scheduler.d.ts +83 -0
- package/dist/automation/compliance-scheduler.d.ts.map +1 -0
- package/dist/automation/compliance-scheduler.js +414 -0
- package/dist/automation/dashboard.d.ts +194 -0
- package/dist/automation/dashboard.d.ts.map +1 -0
- package/dist/automation/dashboard.js +768 -0
- package/dist/automation/email-service.d.ts +69 -0
- package/dist/automation/email-service.d.ts.map +1 -0
- package/dist/automation/email-service.js +218 -0
- package/dist/automation/evidence-collector.d.ts +140 -0
- package/dist/automation/evidence-collector.d.ts.map +1 -0
- package/dist/automation/evidence-collector.js +682 -0
- package/dist/automation/index.d.ts +8 -0
- package/dist/automation/index.d.ts.map +1 -0
- package/dist/automation/index.js +24 -0
- package/dist/automation/pdf-exporter.d.ts +90 -0
- package/dist/automation/pdf-exporter.d.ts.map +1 -0
- package/dist/automation/pdf-exporter.js +381 -0
- package/dist/automation/reporting-engine.d.ts +116 -0
- package/dist/automation/reporting-engine.d.ts.map +1 -0
- package/dist/automation/reporting-engine.js +329 -0
- package/dist/container/index.d.ts +4 -0
- package/dist/container/index.d.ts.map +1 -0
- package/dist/container/index.js +19 -0
- package/dist/container/kubernetes.d.ts +94 -0
- package/dist/container/kubernetes.d.ts.map +1 -0
- package/dist/container/kubernetes.js +268 -0
- package/dist/container/rules.d.ts +27 -0
- package/dist/container/rules.d.ts.map +1 -0
- package/dist/container/rules.js +216 -0
- package/dist/container/scanner.d.ts +50 -0
- package/dist/container/scanner.d.ts.map +1 -0
- package/dist/container/scanner.js +143 -0
- package/dist/frameworks/engine.d.ts +108 -0
- package/dist/frameworks/engine.d.ts.map +1 -0
- package/dist/frameworks/engine.js +206 -0
- package/dist/frameworks/gdpr.d.ts +6 -0
- package/dist/frameworks/gdpr.d.ts.map +1 -0
- package/dist/frameworks/gdpr.js +198 -0
- package/dist/frameworks/hipaa.d.ts +6 -0
- package/dist/frameworks/hipaa.d.ts.map +1 -0
- package/dist/frameworks/hipaa.js +183 -0
- package/dist/frameworks/index.d.ts +8 -0
- package/dist/frameworks/index.d.ts.map +1 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/iso27001.d.ts +63 -0
- package/dist/frameworks/iso27001.d.ts.map +1 -0
- package/dist/frameworks/iso27001.js +331 -0
- package/dist/frameworks/nist.d.ts +62 -0
- package/dist/frameworks/nist.d.ts.map +1 -0
- package/dist/frameworks/nist.js +424 -0
- package/dist/frameworks/pci.d.ts +6 -0
- package/dist/frameworks/pci.d.ts.map +1 -0
- package/dist/frameworks/pci.js +201 -0
- package/dist/frameworks/soc2.d.ts +7 -0
- package/dist/frameworks/soc2.d.ts.map +1 -0
- package/dist/frameworks/soc2.js +248 -0
- package/dist/iac/drift-detector.d.ts +64 -0
- package/dist/iac/drift-detector.d.ts.map +1 -0
- package/dist/iac/drift-detector.js +134 -0
- package/dist/iac/index.d.ts +4 -0
- package/dist/iac/index.d.ts.map +1 -0
- package/dist/iac/index.js +19 -0
- package/dist/iac/rules.d.ts +17 -0
- package/dist/iac/rules.d.ts.map +1 -0
- package/dist/iac/rules.js +385 -0
- package/dist/iac/scanner.d.ts +104 -0
- package/dist/iac/scanner.d.ts.map +1 -0
- package/dist/iac/scanner.js +343 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/pii/data-flow.d.ts +58 -0
- package/dist/pii/data-flow.d.ts.map +1 -0
- package/dist/pii/data-flow.js +154 -0
- package/dist/pii/detector.d.ts +60 -0
- package/dist/pii/detector.d.ts.map +1 -0
- package/dist/pii/detector.js +267 -0
- package/dist/pii/index.d.ts +4 -0
- package/dist/pii/index.d.ts.map +1 -0
- package/dist/pii/index.js +19 -0
- package/dist/pii/patterns.d.ts +36 -0
- package/dist/pii/patterns.d.ts.map +1 -0
- package/dist/pii/patterns.js +108 -0
- package/dist/policy/index.d.ts +5 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +20 -0
- package/dist/policy/opa-engine.d.ts +121 -0
- package/dist/policy/opa-engine.d.ts.map +1 -0
- package/dist/policy/opa-engine.js +423 -0
- package/package.json +31 -0
- package/src/audit/emitter.ts +383 -0
- package/src/audit/events.ts +351 -0
- package/src/audit/index.ts +35 -0
- package/src/audit/storage.ts +394 -0
- package/src/automation/__tests__/compliance-scheduler.test.ts +183 -0
- package/src/automation/audit-logger.ts +629 -0
- package/src/automation/compliance-scheduler-fixed.ts +0 -0
- package/src/automation/compliance-scheduler.ts +516 -0
- package/src/automation/dashboard.ts +947 -0
- package/src/automation/email-service.ts +230 -0
- package/src/automation/evidence-collector.ts +866 -0
- package/src/automation/index.ts +8 -0
- package/src/automation/pdf-exporter.ts +434 -0
- package/src/automation/reporting-engine.ts +462 -0
- package/src/container/index.ts +3 -0
- package/src/container/kubernetes.ts +379 -0
- package/src/container/rules.ts +244 -0
- package/src/container/scanner.ts +202 -0
- package/src/frameworks/engine.ts +298 -0
- package/src/frameworks/gdpr.ts +204 -0
- package/src/frameworks/hipaa.ts +209 -0
- package/src/frameworks/index.ts +23 -0
- package/src/frameworks/iso27001.ts +398 -0
- package/src/frameworks/nist.ts +518 -0
- package/src/frameworks/pci.ts +226 -0
- package/src/frameworks/soc2.ts +281 -0
- package/src/iac/drift-detector.ts +197 -0
- package/src/iac/index.ts +3 -0
- package/src/iac/rules.ts +420 -0
- package/src/iac/scanner.ts +445 -0
- package/src/index.ts +17 -0
- package/src/pii/data-flow.ts +216 -0
- package/src/pii/detector.ts +327 -0
- package/src/pii/index.ts +3 -0
- package/src/pii/patterns.ts +128 -0
- package/src/policy/index.ts +5 -0
- package/src/policy/opa-engine.ts +504 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Export all automation components
|
|
2
|
+
export * from './compliance-scheduler';
|
|
3
|
+
export * from './evidence-collector';
|
|
4
|
+
export * from './audit-logger';
|
|
5
|
+
export * from './reporting-engine';
|
|
6
|
+
export * from './pdf-exporter';
|
|
7
|
+
export * from './dashboard';
|
|
8
|
+
export * from './email-service';
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF Compliance Report Exporter
|
|
3
|
+
*
|
|
4
|
+
* Generates professional PDF reports for compliance assessments.
|
|
5
|
+
* Uses a text-based approach that can be enhanced with a PDF library.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
interface PDFReportData {
|
|
12
|
+
id: string;
|
|
13
|
+
title: string;
|
|
14
|
+
projectName: string;
|
|
15
|
+
frameworkId: string;
|
|
16
|
+
generatedAt: Date;
|
|
17
|
+
summary: {
|
|
18
|
+
overallScore: number;
|
|
19
|
+
status: string;
|
|
20
|
+
totalControls: number;
|
|
21
|
+
compliantControls: number;
|
|
22
|
+
partialControls: number;
|
|
23
|
+
nonCompliantControls: number;
|
|
24
|
+
highRiskGaps: number;
|
|
25
|
+
mediumRiskGaps: number;
|
|
26
|
+
lowRiskGaps: number;
|
|
27
|
+
};
|
|
28
|
+
sections: Array<{
|
|
29
|
+
title: string;
|
|
30
|
+
content: any;
|
|
31
|
+
}>;
|
|
32
|
+
recommendations: Array<{
|
|
33
|
+
priority: string;
|
|
34
|
+
title: string;
|
|
35
|
+
description: string;
|
|
36
|
+
status: string;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface PDFExportOptions {
|
|
41
|
+
outputPath?: string;
|
|
42
|
+
includeHeader?: boolean;
|
|
43
|
+
includeFooter?: boolean;
|
|
44
|
+
includeTableOfContents?: boolean;
|
|
45
|
+
watermark?: string;
|
|
46
|
+
companyName?: string;
|
|
47
|
+
companyLogo?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* PDF Compliance Report Exporter
|
|
52
|
+
*
|
|
53
|
+
* Generates formatted compliance reports that can be exported as PDF.
|
|
54
|
+
* For production use, integrate with a PDF library like pdfkit or puppeteer.
|
|
55
|
+
*/
|
|
56
|
+
export class PDFExporter {
|
|
57
|
+
/**
|
|
58
|
+
* Export compliance report to PDF-ready format
|
|
59
|
+
*/
|
|
60
|
+
async exportToPDF(
|
|
61
|
+
data: PDFReportData,
|
|
62
|
+
options: PDFExportOptions = {}
|
|
63
|
+
): Promise<{ content: string; filename: string; path?: string }> {
|
|
64
|
+
const {
|
|
65
|
+
outputPath,
|
|
66
|
+
includeHeader = true,
|
|
67
|
+
includeFooter = true,
|
|
68
|
+
includeTableOfContents = true,
|
|
69
|
+
watermark,
|
|
70
|
+
companyName = 'Guardrail AI',
|
|
71
|
+
} = options;
|
|
72
|
+
|
|
73
|
+
// Generate PDF content as structured text (HTML-like for rendering)
|
|
74
|
+
const content = this.generatePDFContent(data, {
|
|
75
|
+
includeHeader,
|
|
76
|
+
includeFooter,
|
|
77
|
+
includeTableOfContents,
|
|
78
|
+
watermark,
|
|
79
|
+
companyName,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const filename = `compliance-report-${data.frameworkId}-${Date.now()}.pdf`;
|
|
83
|
+
|
|
84
|
+
// If output path specified, write file
|
|
85
|
+
if (outputPath) {
|
|
86
|
+
const fullPath = path.join(outputPath, filename);
|
|
87
|
+
await this.writeHTMLReport(fullPath.replace('.pdf', '.html'), content);
|
|
88
|
+
|
|
89
|
+
// For actual PDF generation, you would use:
|
|
90
|
+
// await this.generatePDFFile(fullPath, content);
|
|
91
|
+
|
|
92
|
+
return { content, filename, path: fullPath };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { content, filename };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Generate PDF content as structured HTML
|
|
100
|
+
*/
|
|
101
|
+
private generatePDFContent(
|
|
102
|
+
data: PDFReportData,
|
|
103
|
+
options: {
|
|
104
|
+
includeHeader: boolean;
|
|
105
|
+
includeFooter: boolean;
|
|
106
|
+
includeTableOfContents: boolean;
|
|
107
|
+
watermark?: string;
|
|
108
|
+
companyName: string;
|
|
109
|
+
}
|
|
110
|
+
): string {
|
|
111
|
+
const lines: string[] = [];
|
|
112
|
+
|
|
113
|
+
// Document header
|
|
114
|
+
lines.push('<!DOCTYPE html>');
|
|
115
|
+
lines.push('<html lang="en">');
|
|
116
|
+
lines.push('<head>');
|
|
117
|
+
lines.push(' <meta charset="UTF-8">');
|
|
118
|
+
lines.push(` <title>${data.title}</title>`);
|
|
119
|
+
lines.push(' <style>');
|
|
120
|
+
lines.push(this.getStyles());
|
|
121
|
+
lines.push(' </style>');
|
|
122
|
+
lines.push('</head>');
|
|
123
|
+
lines.push('<body>');
|
|
124
|
+
|
|
125
|
+
// Watermark
|
|
126
|
+
if (options.watermark) {
|
|
127
|
+
lines.push(`<div class="watermark">${options.watermark}</div>`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Header
|
|
131
|
+
if (options.includeHeader) {
|
|
132
|
+
lines.push('<header class="report-header">');
|
|
133
|
+
lines.push(` <div class="company-name">${options.companyName}</div>`);
|
|
134
|
+
lines.push(` <h1>${data.title}</h1>`);
|
|
135
|
+
lines.push(` <div class="report-meta">`);
|
|
136
|
+
lines.push(` <span>Project: ${data.projectName}</span>`);
|
|
137
|
+
lines.push(` <span>Framework: ${data.frameworkId.toUpperCase()}</span>`);
|
|
138
|
+
lines.push(` <span>Generated: ${data.generatedAt.toLocaleDateString()}</span>`);
|
|
139
|
+
lines.push(` </div>`);
|
|
140
|
+
lines.push('</header>');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Table of Contents
|
|
144
|
+
if (options.includeTableOfContents) {
|
|
145
|
+
lines.push('<nav class="toc">');
|
|
146
|
+
lines.push(' <h2>Table of Contents</h2>');
|
|
147
|
+
lines.push(' <ol>');
|
|
148
|
+
lines.push(' <li><a href="#executive-summary">Executive Summary</a></li>');
|
|
149
|
+
lines.push(' <li><a href="#compliance-score">Compliance Score</a></li>');
|
|
150
|
+
lines.push(' <li><a href="#control-assessment">Control Assessment</a></li>');
|
|
151
|
+
lines.push(' <li><a href="#gaps-risks">Gaps & Risks</a></li>');
|
|
152
|
+
lines.push(' <li><a href="#recommendations">Recommendations</a></li>');
|
|
153
|
+
lines.push(' </ol>');
|
|
154
|
+
lines.push('</nav>');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Executive Summary
|
|
158
|
+
lines.push('<section id="executive-summary" class="section">');
|
|
159
|
+
lines.push(' <h2>1. Executive Summary</h2>');
|
|
160
|
+
lines.push(' <div class="summary-box">');
|
|
161
|
+
lines.push(` <div class="score-badge ${this.getStatusClass(data.summary.status)}">`);
|
|
162
|
+
lines.push(` <span class="score">${data.summary.overallScore}%</span>`);
|
|
163
|
+
lines.push(` <span class="status">${data.summary.status.toUpperCase()}</span>`);
|
|
164
|
+
lines.push(' </div>');
|
|
165
|
+
lines.push(' <p>');
|
|
166
|
+
lines.push(` This compliance assessment evaluated <strong>${data.summary.totalControls}</strong> controls `);
|
|
167
|
+
lines.push(` against the <strong>${data.frameworkId.toUpperCase()}</strong> framework requirements.`);
|
|
168
|
+
lines.push(' </p>');
|
|
169
|
+
lines.push(' </div>');
|
|
170
|
+
lines.push('</section>');
|
|
171
|
+
|
|
172
|
+
// Compliance Score Breakdown
|
|
173
|
+
lines.push('<section id="compliance-score" class="section">');
|
|
174
|
+
lines.push(' <h2>2. Compliance Score Breakdown</h2>');
|
|
175
|
+
lines.push(' <table class="score-table">');
|
|
176
|
+
lines.push(' <thead>');
|
|
177
|
+
lines.push(' <tr><th>Category</th><th>Count</th><th>Percentage</th></tr>');
|
|
178
|
+
lines.push(' </thead>');
|
|
179
|
+
lines.push(' <tbody>');
|
|
180
|
+
lines.push(` <tr class="compliant"><td>✅ Compliant</td><td>${data.summary.compliantControls}</td><td>${this.percentage(data.summary.compliantControls, data.summary.totalControls)}%</td></tr>`);
|
|
181
|
+
lines.push(` <tr class="partial"><td>⚠️ Partial</td><td>${data.summary.partialControls}</td><td>${this.percentage(data.summary.partialControls, data.summary.totalControls)}%</td></tr>`);
|
|
182
|
+
lines.push(` <tr class="non-compliant"><td>❌ Non-Compliant</td><td>${data.summary.nonCompliantControls}</td><td>${this.percentage(data.summary.nonCompliantControls, data.summary.totalControls)}%</td></tr>`);
|
|
183
|
+
lines.push(' </tbody>');
|
|
184
|
+
lines.push(' </table>');
|
|
185
|
+
lines.push('</section>');
|
|
186
|
+
|
|
187
|
+
// Gaps & Risks
|
|
188
|
+
lines.push('<section id="gaps-risks" class="section">');
|
|
189
|
+
lines.push(' <h2>3. Gaps & Risk Assessment</h2>');
|
|
190
|
+
lines.push(' <div class="risk-summary">');
|
|
191
|
+
lines.push(` <div class="risk-item high"><span class="count">${data.summary.highRiskGaps}</span><span class="label">High Risk</span></div>`);
|
|
192
|
+
lines.push(` <div class="risk-item medium"><span class="count">${data.summary.mediumRiskGaps}</span><span class="label">Medium Risk</span></div>`);
|
|
193
|
+
lines.push(` <div class="risk-item low"><span class="count">${data.summary.lowRiskGaps}</span><span class="label">Low Risk</span></div>`);
|
|
194
|
+
lines.push(' </div>');
|
|
195
|
+
lines.push('</section>');
|
|
196
|
+
|
|
197
|
+
// Recommendations
|
|
198
|
+
lines.push('<section id="recommendations" class="section">');
|
|
199
|
+
lines.push(' <h2>4. Recommendations</h2>');
|
|
200
|
+
if (data.recommendations.length === 0) {
|
|
201
|
+
lines.push(' <p class="no-items">No critical recommendations at this time.</p>');
|
|
202
|
+
} else {
|
|
203
|
+
lines.push(' <div class="recommendations-list">');
|
|
204
|
+
for (const rec of data.recommendations.slice(0, 10)) {
|
|
205
|
+
lines.push(` <div class="recommendation ${rec.priority}">`);
|
|
206
|
+
lines.push(` <div class="rec-header">`);
|
|
207
|
+
lines.push(` <span class="priority-badge">${rec.priority.toUpperCase()}</span>`);
|
|
208
|
+
lines.push(` <span class="rec-title">${rec.title}</span>`);
|
|
209
|
+
lines.push(` </div>`);
|
|
210
|
+
lines.push(` <p class="rec-description">${rec.description}</p>`);
|
|
211
|
+
lines.push(` <span class="rec-status">Status: ${rec.status}</span>`);
|
|
212
|
+
lines.push(' </div>');
|
|
213
|
+
}
|
|
214
|
+
if (data.recommendations.length > 10) {
|
|
215
|
+
lines.push(` <p class="more-items">...and ${data.recommendations.length - 10} more recommendations</p>`);
|
|
216
|
+
}
|
|
217
|
+
lines.push(' </div>');
|
|
218
|
+
}
|
|
219
|
+
lines.push('</section>');
|
|
220
|
+
|
|
221
|
+
// Footer
|
|
222
|
+
if (options.includeFooter) {
|
|
223
|
+
lines.push('<footer class="report-footer">');
|
|
224
|
+
lines.push(` <p>Generated by ${options.companyName} Compliance Engine</p>`);
|
|
225
|
+
lines.push(` <p>Report ID: ${data.id}</p>`);
|
|
226
|
+
lines.push(` <p>This report is confidential and intended for authorized personnel only.</p>`);
|
|
227
|
+
lines.push('</footer>');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
lines.push('</body>');
|
|
231
|
+
lines.push('</html>');
|
|
232
|
+
|
|
233
|
+
return lines.join('\n');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get CSS styles for the PDF report
|
|
238
|
+
*/
|
|
239
|
+
private getStyles(): string {
|
|
240
|
+
return `
|
|
241
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
242
|
+
body {
|
|
243
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
244
|
+
line-height: 1.6;
|
|
245
|
+
color: #333;
|
|
246
|
+
max-width: 800px;
|
|
247
|
+
margin: 0 auto;
|
|
248
|
+
padding: 40px;
|
|
249
|
+
}
|
|
250
|
+
.watermark {
|
|
251
|
+
position: fixed;
|
|
252
|
+
top: 50%;
|
|
253
|
+
left: 50%;
|
|
254
|
+
transform: translate(-50%, -50%) rotate(-45deg);
|
|
255
|
+
font-size: 100px;
|
|
256
|
+
color: rgba(200, 200, 200, 0.2);
|
|
257
|
+
pointer-events: none;
|
|
258
|
+
z-index: -1;
|
|
259
|
+
}
|
|
260
|
+
.report-header {
|
|
261
|
+
text-align: center;
|
|
262
|
+
border-bottom: 3px solid #2563eb;
|
|
263
|
+
padding-bottom: 20px;
|
|
264
|
+
margin-bottom: 30px;
|
|
265
|
+
}
|
|
266
|
+
.company-name { font-size: 14px; color: #666; margin-bottom: 10px; }
|
|
267
|
+
.report-header h1 { font-size: 28px; color: #1e40af; margin-bottom: 15px; }
|
|
268
|
+
.report-meta { display: flex; justify-content: center; gap: 20px; color: #666; font-size: 14px; }
|
|
269
|
+
.toc { background: #f8fafc; padding: 20px; border-radius: 8px; margin-bottom: 30px; }
|
|
270
|
+
.toc h2 { font-size: 18px; margin-bottom: 10px; }
|
|
271
|
+
.toc ol { padding-left: 20px; }
|
|
272
|
+
.toc li { margin: 5px 0; }
|
|
273
|
+
.toc a { color: #2563eb; text-decoration: none; }
|
|
274
|
+
.section { margin-bottom: 40px; page-break-inside: avoid; }
|
|
275
|
+
.section h2 { font-size: 22px; color: #1e40af; border-bottom: 2px solid #e5e7eb; padding-bottom: 10px; margin-bottom: 20px; }
|
|
276
|
+
.summary-box { display: flex; align-items: center; gap: 30px; }
|
|
277
|
+
.score-badge {
|
|
278
|
+
text-align: center;
|
|
279
|
+
padding: 20px 30px;
|
|
280
|
+
border-radius: 12px;
|
|
281
|
+
min-width: 120px;
|
|
282
|
+
}
|
|
283
|
+
.score-badge.compliant { background: #dcfce7; color: #166534; }
|
|
284
|
+
.score-badge.partial { background: #fef3c7; color: #92400e; }
|
|
285
|
+
.score-badge.non-compliant { background: #fee2e2; color: #991b1b; }
|
|
286
|
+
.score { display: block; font-size: 36px; font-weight: bold; }
|
|
287
|
+
.status { display: block; font-size: 14px; margin-top: 5px; }
|
|
288
|
+
.score-table { width: 100%; border-collapse: collapse; margin-top: 15px; }
|
|
289
|
+
.score-table th, .score-table td { padding: 12px; text-align: left; border: 1px solid #e5e7eb; }
|
|
290
|
+
.score-table th { background: #f1f5f9; font-weight: 600; }
|
|
291
|
+
.score-table .compliant td:first-child { color: #166534; }
|
|
292
|
+
.score-table .partial td:first-child { color: #92400e; }
|
|
293
|
+
.score-table .non-compliant td:first-child { color: #991b1b; }
|
|
294
|
+
.risk-summary { display: flex; gap: 20px; margin-top: 15px; }
|
|
295
|
+
.risk-item {
|
|
296
|
+
flex: 1;
|
|
297
|
+
text-align: center;
|
|
298
|
+
padding: 20px;
|
|
299
|
+
border-radius: 8px;
|
|
300
|
+
}
|
|
301
|
+
.risk-item.high { background: #fee2e2; color: #991b1b; }
|
|
302
|
+
.risk-item.medium { background: #fef3c7; color: #92400e; }
|
|
303
|
+
.risk-item.low { background: #dbeafe; color: #1e40af; }
|
|
304
|
+
.risk-item .count { display: block; font-size: 32px; font-weight: bold; }
|
|
305
|
+
.risk-item .label { display: block; font-size: 14px; margin-top: 5px; }
|
|
306
|
+
.recommendations-list { display: flex; flex-direction: column; gap: 15px; }
|
|
307
|
+
.recommendation {
|
|
308
|
+
padding: 15px;
|
|
309
|
+
border-radius: 8px;
|
|
310
|
+
border-left: 4px solid;
|
|
311
|
+
background: #f8fafc;
|
|
312
|
+
}
|
|
313
|
+
.recommendation.critical { border-color: #dc2626; }
|
|
314
|
+
.recommendation.high { border-color: #ea580c; }
|
|
315
|
+
.recommendation.medium { border-color: #ca8a04; }
|
|
316
|
+
.recommendation.low { border-color: #2563eb; }
|
|
317
|
+
.rec-header { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
|
318
|
+
.priority-badge {
|
|
319
|
+
font-size: 11px;
|
|
320
|
+
padding: 2px 8px;
|
|
321
|
+
border-radius: 4px;
|
|
322
|
+
background: #e5e7eb;
|
|
323
|
+
font-weight: 600;
|
|
324
|
+
}
|
|
325
|
+
.recommendation.critical .priority-badge { background: #fee2e2; color: #991b1b; }
|
|
326
|
+
.recommendation.high .priority-badge { background: #ffedd5; color: #9a3412; }
|
|
327
|
+
.recommendation.medium .priority-badge { background: #fef3c7; color: #92400e; }
|
|
328
|
+
.recommendation.low .priority-badge { background: #dbeafe; color: #1e40af; }
|
|
329
|
+
.rec-title { font-weight: 600; }
|
|
330
|
+
.rec-description { color: #666; font-size: 14px; margin-bottom: 8px; }
|
|
331
|
+
.rec-status { font-size: 12px; color: #888; }
|
|
332
|
+
.no-items { color: #666; font-style: italic; }
|
|
333
|
+
.more-items { color: #666; font-size: 14px; margin-top: 10px; }
|
|
334
|
+
.report-footer {
|
|
335
|
+
margin-top: 50px;
|
|
336
|
+
padding-top: 20px;
|
|
337
|
+
border-top: 2px solid #e5e7eb;
|
|
338
|
+
text-align: center;
|
|
339
|
+
color: #666;
|
|
340
|
+
font-size: 12px;
|
|
341
|
+
}
|
|
342
|
+
.report-footer p { margin: 5px 0; }
|
|
343
|
+
@media print {
|
|
344
|
+
body { padding: 20px; }
|
|
345
|
+
.section { page-break-inside: avoid; }
|
|
346
|
+
}
|
|
347
|
+
`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Write HTML report to file
|
|
352
|
+
*/
|
|
353
|
+
private async writeHTMLReport(filePath: string, content: string): Promise<void> {
|
|
354
|
+
const dir = path.dirname(filePath);
|
|
355
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
356
|
+
await fs.promises.writeFile(filePath, content, 'utf8');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Get status class for styling
|
|
361
|
+
*/
|
|
362
|
+
private getStatusClass(status: string): string {
|
|
363
|
+
if (status === 'compliant') return 'compliant';
|
|
364
|
+
if (status === 'partial') return 'partial';
|
|
365
|
+
return 'non-compliant';
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Calculate percentage
|
|
370
|
+
*/
|
|
371
|
+
private percentage(value: number, total: number): number {
|
|
372
|
+
if (total === 0) return 0;
|
|
373
|
+
return Math.round((value / total) * 100);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Export report as JSON
|
|
378
|
+
*/
|
|
379
|
+
async exportToJSON(data: PDFReportData, outputPath?: string): Promise<string> {
|
|
380
|
+
const json = JSON.stringify(data, null, 2);
|
|
381
|
+
|
|
382
|
+
if (outputPath) {
|
|
383
|
+
const filename = `compliance-report-${data.frameworkId}-${Date.now()}.json`;
|
|
384
|
+
const fullPath = path.join(outputPath, filename);
|
|
385
|
+
await fs.promises.mkdir(outputPath, { recursive: true });
|
|
386
|
+
await fs.promises.writeFile(fullPath, json, 'utf8');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return json;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Export report as CSV
|
|
394
|
+
*/
|
|
395
|
+
async exportToCSV(data: PDFReportData, outputPath?: string): Promise<string> {
|
|
396
|
+
const lines: string[] = [];
|
|
397
|
+
|
|
398
|
+
// Header
|
|
399
|
+
lines.push('Category,Count,Percentage,Status');
|
|
400
|
+
|
|
401
|
+
// Summary data
|
|
402
|
+
const total = data.summary.totalControls;
|
|
403
|
+
lines.push(`Compliant,${data.summary.compliantControls},${this.percentage(data.summary.compliantControls, total)}%,Pass`);
|
|
404
|
+
lines.push(`Partial,${data.summary.partialControls},${this.percentage(data.summary.partialControls, total)}%,Warning`);
|
|
405
|
+
lines.push(`Non-Compliant,${data.summary.nonCompliantControls},${this.percentage(data.summary.nonCompliantControls, total)}%,Fail`);
|
|
406
|
+
|
|
407
|
+
lines.push('');
|
|
408
|
+
lines.push('Risk Level,Count');
|
|
409
|
+
lines.push(`High,${data.summary.highRiskGaps}`);
|
|
410
|
+
lines.push(`Medium,${data.summary.mediumRiskGaps}`);
|
|
411
|
+
lines.push(`Low,${data.summary.lowRiskGaps}`);
|
|
412
|
+
|
|
413
|
+
if (data.recommendations.length > 0) {
|
|
414
|
+
lines.push('');
|
|
415
|
+
lines.push('Priority,Title,Description,Status');
|
|
416
|
+
for (const rec of data.recommendations) {
|
|
417
|
+
lines.push(`${rec.priority},"${rec.title}","${rec.description}",${rec.status}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const csv = lines.join('\n');
|
|
422
|
+
|
|
423
|
+
if (outputPath) {
|
|
424
|
+
const filename = `compliance-report-${data.frameworkId}-${Date.now()}.csv`;
|
|
425
|
+
const fullPath = path.join(outputPath, filename);
|
|
426
|
+
await fs.promises.mkdir(outputPath, { recursive: true });
|
|
427
|
+
await fs.promises.writeFile(fullPath, csv, 'utf8');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return csv;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export const pdfExporter = new PDFExporter();
|