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.
Files changed (149) hide show
  1. package/dist/audit/emitter.d.ts +97 -0
  2. package/dist/audit/emitter.d.ts.map +1 -0
  3. package/dist/audit/emitter.js +197 -0
  4. package/dist/audit/events.d.ts +304 -0
  5. package/dist/audit/events.d.ts.map +1 -0
  6. package/dist/audit/events.js +267 -0
  7. package/dist/audit/index.d.ts +11 -0
  8. package/dist/audit/index.d.ts.map +1 -0
  9. package/dist/audit/index.js +51 -0
  10. package/dist/audit/storage.d.ts +93 -0
  11. package/dist/audit/storage.d.ts.map +1 -0
  12. package/dist/audit/storage.js +337 -0
  13. package/dist/automation/__tests__/compliance-scheduler.test.d.ts +2 -0
  14. package/dist/automation/__tests__/compliance-scheduler.test.d.ts.map +1 -0
  15. package/dist/automation/__tests__/compliance-scheduler.test.js +140 -0
  16. package/dist/automation/audit-logger.d.ts +129 -0
  17. package/dist/automation/audit-logger.d.ts.map +1 -0
  18. package/dist/automation/audit-logger.js +473 -0
  19. package/dist/automation/compliance-scheduler-fixed.d.ts +1 -0
  20. package/dist/automation/compliance-scheduler-fixed.d.ts.map +1 -0
  21. package/dist/automation/compliance-scheduler-fixed.js +1 -0
  22. package/dist/automation/compliance-scheduler.d.ts +83 -0
  23. package/dist/automation/compliance-scheduler.d.ts.map +1 -0
  24. package/dist/automation/compliance-scheduler.js +414 -0
  25. package/dist/automation/dashboard.d.ts +194 -0
  26. package/dist/automation/dashboard.d.ts.map +1 -0
  27. package/dist/automation/dashboard.js +768 -0
  28. package/dist/automation/email-service.d.ts +69 -0
  29. package/dist/automation/email-service.d.ts.map +1 -0
  30. package/dist/automation/email-service.js +218 -0
  31. package/dist/automation/evidence-collector.d.ts +140 -0
  32. package/dist/automation/evidence-collector.d.ts.map +1 -0
  33. package/dist/automation/evidence-collector.js +682 -0
  34. package/dist/automation/index.d.ts +8 -0
  35. package/dist/automation/index.d.ts.map +1 -0
  36. package/dist/automation/index.js +24 -0
  37. package/dist/automation/pdf-exporter.d.ts +90 -0
  38. package/dist/automation/pdf-exporter.d.ts.map +1 -0
  39. package/dist/automation/pdf-exporter.js +381 -0
  40. package/dist/automation/reporting-engine.d.ts +116 -0
  41. package/dist/automation/reporting-engine.d.ts.map +1 -0
  42. package/dist/automation/reporting-engine.js +329 -0
  43. package/dist/container/index.d.ts +4 -0
  44. package/dist/container/index.d.ts.map +1 -0
  45. package/dist/container/index.js +19 -0
  46. package/dist/container/kubernetes.d.ts +94 -0
  47. package/dist/container/kubernetes.d.ts.map +1 -0
  48. package/dist/container/kubernetes.js +268 -0
  49. package/dist/container/rules.d.ts +27 -0
  50. package/dist/container/rules.d.ts.map +1 -0
  51. package/dist/container/rules.js +216 -0
  52. package/dist/container/scanner.d.ts +50 -0
  53. package/dist/container/scanner.d.ts.map +1 -0
  54. package/dist/container/scanner.js +143 -0
  55. package/dist/frameworks/engine.d.ts +108 -0
  56. package/dist/frameworks/engine.d.ts.map +1 -0
  57. package/dist/frameworks/engine.js +206 -0
  58. package/dist/frameworks/gdpr.d.ts +6 -0
  59. package/dist/frameworks/gdpr.d.ts.map +1 -0
  60. package/dist/frameworks/gdpr.js +198 -0
  61. package/dist/frameworks/hipaa.d.ts +6 -0
  62. package/dist/frameworks/hipaa.d.ts.map +1 -0
  63. package/dist/frameworks/hipaa.js +183 -0
  64. package/dist/frameworks/index.d.ts +8 -0
  65. package/dist/frameworks/index.d.ts.map +1 -0
  66. package/dist/frameworks/index.js +30 -0
  67. package/dist/frameworks/iso27001.d.ts +63 -0
  68. package/dist/frameworks/iso27001.d.ts.map +1 -0
  69. package/dist/frameworks/iso27001.js +331 -0
  70. package/dist/frameworks/nist.d.ts +62 -0
  71. package/dist/frameworks/nist.d.ts.map +1 -0
  72. package/dist/frameworks/nist.js +424 -0
  73. package/dist/frameworks/pci.d.ts +6 -0
  74. package/dist/frameworks/pci.d.ts.map +1 -0
  75. package/dist/frameworks/pci.js +201 -0
  76. package/dist/frameworks/soc2.d.ts +7 -0
  77. package/dist/frameworks/soc2.d.ts.map +1 -0
  78. package/dist/frameworks/soc2.js +248 -0
  79. package/dist/iac/drift-detector.d.ts +64 -0
  80. package/dist/iac/drift-detector.d.ts.map +1 -0
  81. package/dist/iac/drift-detector.js +134 -0
  82. package/dist/iac/index.d.ts +4 -0
  83. package/dist/iac/index.d.ts.map +1 -0
  84. package/dist/iac/index.js +19 -0
  85. package/dist/iac/rules.d.ts +17 -0
  86. package/dist/iac/rules.d.ts.map +1 -0
  87. package/dist/iac/rules.js +385 -0
  88. package/dist/iac/scanner.d.ts +104 -0
  89. package/dist/iac/scanner.d.ts.map +1 -0
  90. package/dist/iac/scanner.js +343 -0
  91. package/dist/index.d.ts +7 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +28 -0
  94. package/dist/pii/data-flow.d.ts +58 -0
  95. package/dist/pii/data-flow.d.ts.map +1 -0
  96. package/dist/pii/data-flow.js +154 -0
  97. package/dist/pii/detector.d.ts +60 -0
  98. package/dist/pii/detector.d.ts.map +1 -0
  99. package/dist/pii/detector.js +267 -0
  100. package/dist/pii/index.d.ts +4 -0
  101. package/dist/pii/index.d.ts.map +1 -0
  102. package/dist/pii/index.js +19 -0
  103. package/dist/pii/patterns.d.ts +36 -0
  104. package/dist/pii/patterns.d.ts.map +1 -0
  105. package/dist/pii/patterns.js +108 -0
  106. package/dist/policy/index.d.ts +5 -0
  107. package/dist/policy/index.d.ts.map +1 -0
  108. package/dist/policy/index.js +20 -0
  109. package/dist/policy/opa-engine.d.ts +121 -0
  110. package/dist/policy/opa-engine.d.ts.map +1 -0
  111. package/dist/policy/opa-engine.js +423 -0
  112. package/package.json +31 -0
  113. package/src/audit/emitter.ts +383 -0
  114. package/src/audit/events.ts +351 -0
  115. package/src/audit/index.ts +35 -0
  116. package/src/audit/storage.ts +394 -0
  117. package/src/automation/__tests__/compliance-scheduler.test.ts +183 -0
  118. package/src/automation/audit-logger.ts +629 -0
  119. package/src/automation/compliance-scheduler-fixed.ts +0 -0
  120. package/src/automation/compliance-scheduler.ts +516 -0
  121. package/src/automation/dashboard.ts +947 -0
  122. package/src/automation/email-service.ts +230 -0
  123. package/src/automation/evidence-collector.ts +866 -0
  124. package/src/automation/index.ts +8 -0
  125. package/src/automation/pdf-exporter.ts +434 -0
  126. package/src/automation/reporting-engine.ts +462 -0
  127. package/src/container/index.ts +3 -0
  128. package/src/container/kubernetes.ts +379 -0
  129. package/src/container/rules.ts +244 -0
  130. package/src/container/scanner.ts +202 -0
  131. package/src/frameworks/engine.ts +298 -0
  132. package/src/frameworks/gdpr.ts +204 -0
  133. package/src/frameworks/hipaa.ts +209 -0
  134. package/src/frameworks/index.ts +23 -0
  135. package/src/frameworks/iso27001.ts +398 -0
  136. package/src/frameworks/nist.ts +518 -0
  137. package/src/frameworks/pci.ts +226 -0
  138. package/src/frameworks/soc2.ts +281 -0
  139. package/src/iac/drift-detector.ts +197 -0
  140. package/src/iac/index.ts +3 -0
  141. package/src/iac/rules.ts +420 -0
  142. package/src/iac/scanner.ts +445 -0
  143. package/src/index.ts +17 -0
  144. package/src/pii/data-flow.ts +216 -0
  145. package/src/pii/detector.ts +327 -0
  146. package/src/pii/index.ts +3 -0
  147. package/src/pii/patterns.ts +128 -0
  148. package/src/policy/index.ts +5 -0
  149. 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();