tlc-claude-code 1.4.8 → 1.4.9
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/package.json +1 -1
- package/server/index.js +229 -14
- package/server/lib/compliance/control-mapper.js +401 -0
- package/server/lib/compliance/control-mapper.test.js +117 -0
- package/server/lib/compliance/evidence-linker.js +296 -0
- package/server/lib/compliance/evidence-linker.test.js +121 -0
- package/server/lib/compliance/gdpr-checklist.js +416 -0
- package/server/lib/compliance/gdpr-checklist.test.js +131 -0
- package/server/lib/compliance/hipaa-checklist.js +277 -0
- package/server/lib/compliance/hipaa-checklist.test.js +101 -0
- package/server/lib/compliance/iso27001-checklist.js +287 -0
- package/server/lib/compliance/iso27001-checklist.test.js +99 -0
- package/server/lib/compliance/multi-framework-reporter.js +284 -0
- package/server/lib/compliance/multi-framework-reporter.test.js +127 -0
- package/server/lib/compliance/pci-dss-checklist.js +214 -0
- package/server/lib/compliance/pci-dss-checklist.test.js +95 -0
- package/server/lib/compliance/trust-centre.js +187 -0
- package/server/lib/compliance/trust-centre.test.js +93 -0
- package/server/lib/dashboard/api-server.js +155 -0
- package/server/lib/dashboard/api-server.test.js +155 -0
- package/server/lib/dashboard/health-api.js +199 -0
- package/server/lib/dashboard/health-api.test.js +122 -0
- package/server/lib/dashboard/notes-api.js +234 -0
- package/server/lib/dashboard/notes-api.test.js +134 -0
- package/server/lib/dashboard/router-api.js +176 -0
- package/server/lib/dashboard/router-api.test.js +132 -0
- package/server/lib/dashboard/tasks-api.js +289 -0
- package/server/lib/dashboard/tasks-api.test.js +161 -0
- package/server/lib/dashboard/tlc-introspection.js +197 -0
- package/server/lib/dashboard/tlc-introspection.test.js +138 -0
- package/server/lib/dashboard/version-api.js +222 -0
- package/server/lib/dashboard/version-api.test.js +112 -0
- package/server/lib/dashboard/websocket-server.js +104 -0
- package/server/lib/dashboard/websocket-server.test.js +118 -0
- package/server/lib/deploy/branch-classifier.js +163 -0
- package/server/lib/deploy/branch-classifier.test.js +164 -0
- package/server/lib/deploy/deployment-approval.js +299 -0
- package/server/lib/deploy/deployment-approval.test.js +296 -0
- package/server/lib/deploy/deployment-audit.js +374 -0
- package/server/lib/deploy/deployment-audit.test.js +307 -0
- package/server/lib/deploy/deployment-executor.js +335 -0
- package/server/lib/deploy/deployment-executor.test.js +329 -0
- package/server/lib/deploy/deployment-rules.js +163 -0
- package/server/lib/deploy/deployment-rules.test.js +188 -0
- package/server/lib/deploy/rollback-manager.js +379 -0
- package/server/lib/deploy/rollback-manager.test.js +321 -0
- package/server/lib/deploy/security-gates.js +236 -0
- package/server/lib/deploy/security-gates.test.js +222 -0
- package/server/lib/k8s/gitops-config.js +188 -0
- package/server/lib/k8s/gitops-config.test.js +59 -0
- package/server/lib/k8s/helm-generator.js +196 -0
- package/server/lib/k8s/helm-generator.test.js +59 -0
- package/server/lib/k8s/kustomize-generator.js +176 -0
- package/server/lib/k8s/kustomize-generator.test.js +58 -0
- package/server/lib/k8s/network-policy.js +114 -0
- package/server/lib/k8s/network-policy.test.js +53 -0
- package/server/lib/k8s/pod-security.js +114 -0
- package/server/lib/k8s/pod-security.test.js +55 -0
- package/server/lib/k8s/rbac-generator.js +132 -0
- package/server/lib/k8s/rbac-generator.test.js +57 -0
- package/server/lib/k8s/resource-manager.js +172 -0
- package/server/lib/k8s/resource-manager.test.js +60 -0
- package/server/lib/k8s/secrets-encryption.js +168 -0
- package/server/lib/k8s/secrets-encryption.test.js +49 -0
- package/server/lib/monitoring/alert-manager.js +238 -0
- package/server/lib/monitoring/alert-manager.test.js +106 -0
- package/server/lib/monitoring/health-check.js +226 -0
- package/server/lib/monitoring/health-check.test.js +176 -0
- package/server/lib/monitoring/incident-manager.js +230 -0
- package/server/lib/monitoring/incident-manager.test.js +98 -0
- package/server/lib/monitoring/log-aggregator.js +147 -0
- package/server/lib/monitoring/log-aggregator.test.js +89 -0
- package/server/lib/monitoring/metrics-collector.js +337 -0
- package/server/lib/monitoring/metrics-collector.test.js +172 -0
- package/server/lib/monitoring/status-page.js +214 -0
- package/server/lib/monitoring/status-page.test.js +105 -0
- package/server/lib/monitoring/uptime-monitor.js +194 -0
- package/server/lib/monitoring/uptime-monitor.test.js +109 -0
- package/server/lib/network/fail2ban-config.js +294 -0
- package/server/lib/network/fail2ban-config.test.js +275 -0
- package/server/lib/network/firewall-manager.js +252 -0
- package/server/lib/network/firewall-manager.test.js +254 -0
- package/server/lib/network/geoip-filter.js +282 -0
- package/server/lib/network/geoip-filter.test.js +264 -0
- package/server/lib/network/rate-limiter.js +229 -0
- package/server/lib/network/rate-limiter.test.js +293 -0
- package/server/lib/network/request-validator.js +351 -0
- package/server/lib/network/request-validator.test.js +345 -0
- package/server/lib/network/security-headers.js +251 -0
- package/server/lib/network/security-headers.test.js +283 -0
- package/server/lib/network/tls-config.js +210 -0
- package/server/lib/network/tls-config.test.js +248 -0
- package/server/lib/security/auth-security.js +369 -0
- package/server/lib/security/auth-security.test.js +448 -0
- package/server/lib/security/cis-benchmark.js +152 -0
- package/server/lib/security/cis-benchmark.test.js +137 -0
- package/server/lib/security/compose-templates.js +312 -0
- package/server/lib/security/compose-templates.test.js +229 -0
- package/server/lib/security/container-runtime.js +456 -0
- package/server/lib/security/container-runtime.test.js +503 -0
- package/server/lib/security/cors-validator.js +278 -0
- package/server/lib/security/cors-validator.test.js +310 -0
- package/server/lib/security/crypto-utils.js +253 -0
- package/server/lib/security/crypto-utils.test.js +409 -0
- package/server/lib/security/dockerfile-linter.js +459 -0
- package/server/lib/security/dockerfile-linter.test.js +483 -0
- package/server/lib/security/dockerfile-templates.js +278 -0
- package/server/lib/security/dockerfile-templates.test.js +164 -0
- package/server/lib/security/error-sanitizer.js +426 -0
- package/server/lib/security/error-sanitizer.test.js +331 -0
- package/server/lib/security/headers-generator.js +368 -0
- package/server/lib/security/headers-generator.test.js +398 -0
- package/server/lib/security/image-scanner.js +83 -0
- package/server/lib/security/image-scanner.test.js +106 -0
- package/server/lib/security/input-validator.js +352 -0
- package/server/lib/security/input-validator.test.js +330 -0
- package/server/lib/security/network-policy.js +174 -0
- package/server/lib/security/network-policy.test.js +164 -0
- package/server/lib/security/output-encoder.js +237 -0
- package/server/lib/security/output-encoder.test.js +276 -0
- package/server/lib/security/path-validator.js +359 -0
- package/server/lib/security/path-validator.test.js +293 -0
- package/server/lib/security/query-builder.js +421 -0
- package/server/lib/security/query-builder.test.js +318 -0
- package/server/lib/security/secret-detector.js +290 -0
- package/server/lib/security/secret-detector.test.js +354 -0
- package/server/lib/security/secrets-validator.js +137 -0
- package/server/lib/security/secrets-validator.test.js +120 -0
- package/server/lib/security-testing/dast-runner.js +154 -0
- package/server/lib/security-testing/dast-runner.test.js +62 -0
- package/server/lib/security-testing/dependency-scanner.js +172 -0
- package/server/lib/security-testing/dependency-scanner.test.js +64 -0
- package/server/lib/security-testing/pentest-runner.js +230 -0
- package/server/lib/security-testing/pentest-runner.test.js +60 -0
- package/server/lib/security-testing/sast-runner.js +136 -0
- package/server/lib/security-testing/sast-runner.test.js +62 -0
- package/server/lib/security-testing/secret-scanner.js +153 -0
- package/server/lib/security-testing/secret-scanner.test.js +66 -0
- package/server/lib/security-testing/security-gate.js +216 -0
- package/server/lib/security-testing/security-gate.test.js +115 -0
- package/server/lib/security-testing/security-reporter.js +303 -0
- package/server/lib/security-testing/security-reporter.test.js +114 -0
- package/server/lib/standards/audit-checker.js +546 -0
- package/server/lib/standards/audit-checker.test.js +415 -0
- package/server/lib/standards/cleanup-executor.js +452 -0
- package/server/lib/standards/cleanup-executor.test.js +293 -0
- package/server/lib/standards/refactor-stepper.js +425 -0
- package/server/lib/standards/refactor-stepper.test.js +298 -0
- package/server/lib/standards/standards-injector.js +167 -0
- package/server/lib/standards/standards-injector.test.js +232 -0
- package/server/lib/user-management.test.js +284 -0
- package/server/lib/vps/backup-manager.js +157 -0
- package/server/lib/vps/backup-manager.test.js +59 -0
- package/server/lib/vps/caddy-config.js +159 -0
- package/server/lib/vps/caddy-config.test.js +48 -0
- package/server/lib/vps/compose-orchestrator.js +219 -0
- package/server/lib/vps/compose-orchestrator.test.js +50 -0
- package/server/lib/vps/database-config.js +208 -0
- package/server/lib/vps/database-config.test.js +47 -0
- package/server/lib/vps/deploy-script.js +211 -0
- package/server/lib/vps/deploy-script.test.js +53 -0
- package/server/lib/vps/secrets-manager.js +148 -0
- package/server/lib/vps/secrets-manager.test.js +58 -0
- package/server/lib/vps/server-hardening.js +174 -0
- package/server/lib/vps/server-hardening.test.js +70 -0
- package/server/package-lock.json +19 -0
- package/server/package.json +1 -0
- package/server/templates/CLAUDE.md +37 -0
- package/server/templates/CODING-STANDARDS.md +408 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ISO 27001 Checklist Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
+
import { createIso27001Checklist, getControls, checkControl, generateSoa, mapAnnexAControls } from './iso27001-checklist.js';
|
|
6
|
+
|
|
7
|
+
describe('iso27001-checklist', () => {
|
|
8
|
+
describe('createIso27001Checklist', () => {
|
|
9
|
+
it('creates ISO 27001 checklist', () => {
|
|
10
|
+
const checklist = createIso27001Checklist();
|
|
11
|
+
expect(checklist.evaluate).toBeDefined();
|
|
12
|
+
expect(checklist.getControls).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('supports ISO 27001:2022', () => {
|
|
16
|
+
const checklist = createIso27001Checklist({ version: '2022' });
|
|
17
|
+
const controls = checklist.getControls();
|
|
18
|
+
// 2022 version has 93 controls in 4 themes
|
|
19
|
+
expect(controls.some(c => c.theme === 'organizational')).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('getControls', () => {
|
|
24
|
+
it('returns all Annex A controls', () => {
|
|
25
|
+
const controls = getControls();
|
|
26
|
+
expect(controls.length).toBeGreaterThan(0);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('groups by control theme', () => {
|
|
30
|
+
const controls = getControls({ groupBy: 'theme' });
|
|
31
|
+
expect(controls.organizational).toBeDefined();
|
|
32
|
+
expect(controls.people).toBeDefined();
|
|
33
|
+
expect(controls.physical).toBeDefined();
|
|
34
|
+
expect(controls.technological).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('includes control attributes', () => {
|
|
38
|
+
const controls = getControls();
|
|
39
|
+
const control = controls[0];
|
|
40
|
+
expect(control.id).toBeDefined();
|
|
41
|
+
expect(control.name).toBeDefined();
|
|
42
|
+
expect(control.purpose).toBeDefined();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('checkControl', () => {
|
|
47
|
+
it('checks control implementation', () => {
|
|
48
|
+
const evidence = { policies: { informationSecurity: true } };
|
|
49
|
+
const result = checkControl('A.5.1', evidence);
|
|
50
|
+
expect(result.controlId).toBe('A.5.1');
|
|
51
|
+
expect(result.implemented).toBeDefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('evaluates control effectiveness', () => {
|
|
55
|
+
const evidence = {
|
|
56
|
+
accessControl: { implemented: true, tested: true, documented: true }
|
|
57
|
+
};
|
|
58
|
+
const result = checkControl('A.5.15', evidence);
|
|
59
|
+
expect(result.effectiveness).toBeDefined();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('identifies missing evidence', () => {
|
|
63
|
+
const result = checkControl('A.5.1', {});
|
|
64
|
+
expect(result.missingEvidence.length).toBeGreaterThan(0);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('generateSoa', () => {
|
|
69
|
+
it('generates Statement of Applicability', () => {
|
|
70
|
+
const assessments = [
|
|
71
|
+
{ controlId: 'A.5.1', applicable: true, implemented: true },
|
|
72
|
+
{ controlId: 'A.5.2', applicable: false, justification: 'No cloud services' }
|
|
73
|
+
];
|
|
74
|
+
const soa = generateSoa(assessments);
|
|
75
|
+
expect(soa).toContain('Statement of Applicability');
|
|
76
|
+
expect(soa).toContain('A.5.1');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('includes justification for excluded controls', () => {
|
|
80
|
+
const assessments = [
|
|
81
|
+
{ controlId: 'A.7.1', applicable: false, justification: 'Remote-only company' }
|
|
82
|
+
];
|
|
83
|
+
const soa = generateSoa(assessments);
|
|
84
|
+
expect(soa).toContain('Remote-only company');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('mapAnnexAControls', () => {
|
|
89
|
+
it('maps to technical implementations', () => {
|
|
90
|
+
const mappings = mapAnnexAControls('A.8.24');
|
|
91
|
+
expect(mappings.some(m => m.type === 'encryption')).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('maps to code patterns', () => {
|
|
95
|
+
const mappings = mapAnnexAControls('A.8.3');
|
|
96
|
+
expect(mappings.some(m => m.type === 'access-control')).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Framework Reporter - Multi-framework compliance reporting
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Framework display names
|
|
6
|
+
const FRAMEWORK_NAMES = {
|
|
7
|
+
'pci-dss': 'PCI DSS',
|
|
8
|
+
'iso27001': 'ISO 27001',
|
|
9
|
+
'hipaa': 'HIPAA',
|
|
10
|
+
'gdpr': 'GDPR',
|
|
11
|
+
'soc2': 'SOC 2'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Unique requirements per framework
|
|
15
|
+
const UNIQUE_REQUIREMENTS = {
|
|
16
|
+
'pci-dss': [
|
|
17
|
+
'Payment card data handling',
|
|
18
|
+
'PAN masking requirements',
|
|
19
|
+
'Quarterly vulnerability scans',
|
|
20
|
+
'Annual penetration testing'
|
|
21
|
+
],
|
|
22
|
+
'hipaa': [
|
|
23
|
+
'Protected Health Information (PHI) handling',
|
|
24
|
+
'Business Associate Agreements',
|
|
25
|
+
'Patient access rights',
|
|
26
|
+
'Breach notification within 60 days'
|
|
27
|
+
],
|
|
28
|
+
'iso27001': [
|
|
29
|
+
'Information Security Management System (ISMS)',
|
|
30
|
+
'Statement of Applicability',
|
|
31
|
+
'Risk treatment plan',
|
|
32
|
+
'Management review'
|
|
33
|
+
]
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create a multi-framework reporter instance
|
|
38
|
+
*/
|
|
39
|
+
export function createMultiFrameworkReporter() {
|
|
40
|
+
const frameworks = [];
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
generate: (status, options) => generateConsolidatedReport(status, options),
|
|
44
|
+
compare: (status, options) => compareFrameworks(status, options),
|
|
45
|
+
addFramework: (framework) => {
|
|
46
|
+
if (!frameworks.includes(framework)) {
|
|
47
|
+
frameworks.push(framework);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
getFrameworks: () => [...frameworks]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generate a consolidated compliance report across frameworks
|
|
56
|
+
*/
|
|
57
|
+
export function generateConsolidatedReport(status, options = {}) {
|
|
58
|
+
let report = `# Compliance Dashboard\n\n`;
|
|
59
|
+
|
|
60
|
+
// Executive summary if requested
|
|
61
|
+
if (options.includeSummary) {
|
|
62
|
+
report += `## Executive Summary\n\n`;
|
|
63
|
+
const frameworks = Object.keys(status);
|
|
64
|
+
const scores = frameworks.map(f => status[f]?.score || 0);
|
|
65
|
+
const avgScore = scores.length > 0 ? Math.round(scores.reduce((a, b) => a + b, 0) / scores.length) : 0;
|
|
66
|
+
report += `- **Frameworks assessed:** ${frameworks.length}\n`;
|
|
67
|
+
report += `- **Average compliance score:** ${avgScore}%\n`;
|
|
68
|
+
report += `- **Assessment date:** ${new Date().toISOString().split('T')[0]}\n\n`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Framework scores table
|
|
72
|
+
report += `## Framework Compliance Status\n\n`;
|
|
73
|
+
report += `| Framework | Score | Gaps | Status |\n`;
|
|
74
|
+
report += `|-----------|-------|------|--------|\n`;
|
|
75
|
+
|
|
76
|
+
for (const [framework, data] of Object.entries(status)) {
|
|
77
|
+
if (data && typeof data === 'object') {
|
|
78
|
+
const name = FRAMEWORK_NAMES[framework] || framework;
|
|
79
|
+
const score = data.score || 0;
|
|
80
|
+
const gaps = Array.isArray(data.gaps) ? data.gaps.length : (data.gaps || 0);
|
|
81
|
+
const statusIcon = score >= 80 ? 'Compliant' : score >= 60 ? 'Partial' : 'Non-compliant';
|
|
82
|
+
report += `| ${name} | ${score}% | ${gaps} | ${statusIcon} |\n`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
report += '\n';
|
|
86
|
+
|
|
87
|
+
// Common gaps if requested
|
|
88
|
+
if (options.highlightCommon) {
|
|
89
|
+
report += `## Common Gaps\n\n`;
|
|
90
|
+
|
|
91
|
+
const gapAreas = {};
|
|
92
|
+
for (const [framework, data] of Object.entries(status)) {
|
|
93
|
+
if (data?.gaps && Array.isArray(data.gaps)) {
|
|
94
|
+
for (const gap of data.gaps) {
|
|
95
|
+
const area = gap.area || gap.control || 'unknown';
|
|
96
|
+
if (!gapAreas[area]) {
|
|
97
|
+
gapAreas[area] = [];
|
|
98
|
+
}
|
|
99
|
+
gapAreas[area].push(framework);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const commonGaps = Object.entries(gapAreas).filter(([, frameworks]) => frameworks.length > 1);
|
|
105
|
+
|
|
106
|
+
if (commonGaps.length > 0) {
|
|
107
|
+
report += `The following gaps appear across multiple frameworks:\n\n`;
|
|
108
|
+
for (const [area, frameworks] of commonGaps) {
|
|
109
|
+
report += `- **${area}**: Affects ${frameworks.map(f => FRAMEWORK_NAMES[f] || f).join(', ')}\n`;
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
report += `No common gaps identified across frameworks.\n`;
|
|
113
|
+
}
|
|
114
|
+
report += '\n';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return report;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Generate a report for a single framework
|
|
122
|
+
*/
|
|
123
|
+
export function generateFrameworkReport(status, options = {}) {
|
|
124
|
+
const framework = status.framework;
|
|
125
|
+
const name = FRAMEWORK_NAMES[framework] || framework;
|
|
126
|
+
const score = status.score || 0;
|
|
127
|
+
|
|
128
|
+
let report = `# ${name} Compliance Report\n\n`;
|
|
129
|
+
report += `**Compliance Score:** ${score}%\n\n`;
|
|
130
|
+
|
|
131
|
+
// Group controls if requested
|
|
132
|
+
if (options.groupBy === 'category' && status.controls) {
|
|
133
|
+
const groups = {};
|
|
134
|
+
|
|
135
|
+
for (const control of status.controls) {
|
|
136
|
+
const category = control.category || 'uncategorized';
|
|
137
|
+
if (!groups[category]) {
|
|
138
|
+
groups[category] = [];
|
|
139
|
+
}
|
|
140
|
+
groups[category].push(control);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const [category, controls] of Object.entries(groups)) {
|
|
144
|
+
const categoryName = category.charAt(0).toUpperCase() + category.slice(1);
|
|
145
|
+
report += `## ${categoryName}\n\n`;
|
|
146
|
+
report += `| Control | Status |\n`;
|
|
147
|
+
report += `|---------|--------|\n`;
|
|
148
|
+
for (const control of controls) {
|
|
149
|
+
report += `| ${control.id} | ${control.status || '-'} |\n`;
|
|
150
|
+
}
|
|
151
|
+
report += '\n';
|
|
152
|
+
}
|
|
153
|
+
} else if (status.controls) {
|
|
154
|
+
report += `## Controls\n\n`;
|
|
155
|
+
report += `| Control | Status |\n`;
|
|
156
|
+
report += `|---------|--------|\n`;
|
|
157
|
+
for (const control of status.controls) {
|
|
158
|
+
report += `| ${control.id} | ${control.status || '-'} |\n`;
|
|
159
|
+
}
|
|
160
|
+
report += '\n';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return report;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Compare compliance across frameworks
|
|
168
|
+
*/
|
|
169
|
+
export function compareFrameworks(status, options = {}) {
|
|
170
|
+
const result = {
|
|
171
|
+
highest: null,
|
|
172
|
+
lowest: null,
|
|
173
|
+
uniqueRequirements: {}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Find highest and lowest scores
|
|
177
|
+
let highestScore = -1;
|
|
178
|
+
let lowestScore = 101;
|
|
179
|
+
|
|
180
|
+
for (const [framework, data] of Object.entries(status)) {
|
|
181
|
+
const score = data?.score || 0;
|
|
182
|
+
if (score > highestScore) {
|
|
183
|
+
highestScore = score;
|
|
184
|
+
result.highest = framework;
|
|
185
|
+
}
|
|
186
|
+
if (score < lowestScore) {
|
|
187
|
+
lowestScore = score;
|
|
188
|
+
result.lowest = framework;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Get unique requirements for specified frameworks
|
|
193
|
+
const frameworks = options.frameworks || Object.keys(status);
|
|
194
|
+
for (const framework of frameworks) {
|
|
195
|
+
result.uniqueRequirements[framework] = UNIQUE_REQUIREMENTS[framework] || [];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Export report in various formats
|
|
203
|
+
* Returns a Promise for PDF (async), synchronous result for other formats
|
|
204
|
+
*/
|
|
205
|
+
export function exportReport(report, options = {}) {
|
|
206
|
+
const format = options.format || 'json';
|
|
207
|
+
|
|
208
|
+
switch (format) {
|
|
209
|
+
case 'pdf':
|
|
210
|
+
if (options.pdfGenerator) {
|
|
211
|
+
// PDF generation is async
|
|
212
|
+
return options.pdfGenerator(report).then(pdfContent => ({
|
|
213
|
+
format: 'pdf',
|
|
214
|
+
content: pdfContent
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
throw new Error('PDF generator function required');
|
|
218
|
+
|
|
219
|
+
case 'html':
|
|
220
|
+
return {
|
|
221
|
+
format: 'html',
|
|
222
|
+
content: generateHtml(report)
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
case 'json':
|
|
226
|
+
return {
|
|
227
|
+
format: 'json',
|
|
228
|
+
content: JSON.stringify(report)
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
case 'markdown':
|
|
232
|
+
return {
|
|
233
|
+
format: 'markdown',
|
|
234
|
+
content: generateMarkdown(report)
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
default:
|
|
238
|
+
throw new Error(`Unsupported export format: ${format}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Generate HTML from report
|
|
244
|
+
*/
|
|
245
|
+
function generateHtml(report) {
|
|
246
|
+
const title = report.title || 'Compliance Report';
|
|
247
|
+
const content = report.content || JSON.stringify(report);
|
|
248
|
+
|
|
249
|
+
return `<!DOCTYPE html>
|
|
250
|
+
<html>
|
|
251
|
+
<head>
|
|
252
|
+
<title>${title}</title>
|
|
253
|
+
<style>
|
|
254
|
+
body { font-family: Arial, sans-serif; margin: 40px; }
|
|
255
|
+
h1 { color: #333; }
|
|
256
|
+
table { border-collapse: collapse; width: 100%; }
|
|
257
|
+
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
|
258
|
+
th { background-color: #f4f4f4; }
|
|
259
|
+
</style>
|
|
260
|
+
</head>
|
|
261
|
+
<body>
|
|
262
|
+
<h1>${title}</h1>
|
|
263
|
+
<div>${content}</div>
|
|
264
|
+
</body>
|
|
265
|
+
</html>`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Generate Markdown from report
|
|
270
|
+
*/
|
|
271
|
+
function generateMarkdown(report) {
|
|
272
|
+
const title = report.title || 'Compliance Report';
|
|
273
|
+
const content = report.content || JSON.stringify(report, null, 2);
|
|
274
|
+
|
|
275
|
+
return `# ${title}\n\n${content}`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export default {
|
|
279
|
+
createMultiFrameworkReporter,
|
|
280
|
+
generateConsolidatedReport,
|
|
281
|
+
generateFrameworkReport,
|
|
282
|
+
compareFrameworks,
|
|
283
|
+
exportReport
|
|
284
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Framework Reporter Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
+
import { createMultiFrameworkReporter, generateConsolidatedReport, generateFrameworkReport, compareFrameworks, exportReport } from './multi-framework-reporter.js';
|
|
6
|
+
|
|
7
|
+
describe('multi-framework-reporter', () => {
|
|
8
|
+
describe('createMultiFrameworkReporter', () => {
|
|
9
|
+
it('creates multi-framework reporter', () => {
|
|
10
|
+
const reporter = createMultiFrameworkReporter();
|
|
11
|
+
expect(reporter.generate).toBeDefined();
|
|
12
|
+
expect(reporter.compare).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('supports multiple frameworks', () => {
|
|
16
|
+
const reporter = createMultiFrameworkReporter();
|
|
17
|
+
reporter.addFramework('pci-dss');
|
|
18
|
+
reporter.addFramework('hipaa');
|
|
19
|
+
reporter.addFramework('iso27001');
|
|
20
|
+
expect(reporter.getFrameworks().length).toBe(3);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('generateConsolidatedReport', () => {
|
|
25
|
+
it('generates consolidated compliance report', () => {
|
|
26
|
+
const status = {
|
|
27
|
+
'pci-dss': { score: 85, gaps: 5 },
|
|
28
|
+
'hipaa': { score: 90, gaps: 3 },
|
|
29
|
+
'iso27001': { score: 78, gaps: 10 }
|
|
30
|
+
};
|
|
31
|
+
const report = generateConsolidatedReport(status);
|
|
32
|
+
expect(report).toContain('Compliance Dashboard');
|
|
33
|
+
expect(report).toContain('PCI DSS');
|
|
34
|
+
expect(report).toContain('HIPAA');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('highlights common gaps', () => {
|
|
38
|
+
const status = {
|
|
39
|
+
'pci-dss': { gaps: [{ area: 'encryption' }] },
|
|
40
|
+
'iso27001': { gaps: [{ area: 'encryption' }] }
|
|
41
|
+
};
|
|
42
|
+
const report = generateConsolidatedReport(status, { highlightCommon: true });
|
|
43
|
+
expect(report).toContain('Common Gaps');
|
|
44
|
+
expect(report).toContain('encryption');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('includes executive summary', () => {
|
|
48
|
+
const report = generateConsolidatedReport({}, { includeSummary: true });
|
|
49
|
+
expect(report).toContain('Executive Summary');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('generateFrameworkReport', () => {
|
|
54
|
+
it('generates single framework report', () => {
|
|
55
|
+
const status = {
|
|
56
|
+
framework: 'pci-dss',
|
|
57
|
+
score: 85,
|
|
58
|
+
controls: [
|
|
59
|
+
{ id: 'req-1.1', status: 'compliant' },
|
|
60
|
+
{ id: 'req-3.4', status: 'non-compliant' }
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
const report = generateFrameworkReport(status);
|
|
64
|
+
expect(report).toContain('PCI DSS');
|
|
65
|
+
expect(report).toContain('85%');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('groups by control category', () => {
|
|
69
|
+
const status = {
|
|
70
|
+
framework: 'iso27001',
|
|
71
|
+
controls: [
|
|
72
|
+
{ id: 'A.5.1', category: 'organizational' },
|
|
73
|
+
{ id: 'A.8.1', category: 'technological' }
|
|
74
|
+
]
|
|
75
|
+
};
|
|
76
|
+
const report = generateFrameworkReport(status, { groupBy: 'category' });
|
|
77
|
+
expect(report).toContain('Organizational');
|
|
78
|
+
expect(report).toContain('Technological');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('compareFrameworks', () => {
|
|
83
|
+
it('compares compliance across frameworks', () => {
|
|
84
|
+
const status = {
|
|
85
|
+
'pci-dss': { score: 85 },
|
|
86
|
+
'hipaa': { score: 90 }
|
|
87
|
+
};
|
|
88
|
+
const comparison = compareFrameworks(status);
|
|
89
|
+
expect(comparison.highest).toBe('hipaa');
|
|
90
|
+
expect(comparison.lowest).toBe('pci-dss');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('identifies unique requirements', () => {
|
|
94
|
+
const comparison = compareFrameworks({}, { frameworks: ['pci-dss', 'hipaa'] });
|
|
95
|
+
expect(comparison.uniqueRequirements['pci-dss']).toBeDefined();
|
|
96
|
+
expect(comparison.uniqueRequirements['hipaa']).toBeDefined();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('exportReport', () => {
|
|
101
|
+
it('exports as PDF', async () => {
|
|
102
|
+
const report = { content: 'Test report' };
|
|
103
|
+
const mockPdfGenerator = vi.fn().mockResolvedValue(Buffer.from('PDF'));
|
|
104
|
+
const result = await exportReport(report, { format: 'pdf', pdfGenerator: mockPdfGenerator });
|
|
105
|
+
expect(result.format).toBe('pdf');
|
|
106
|
+
expect(mockPdfGenerator).toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('exports as HTML', () => {
|
|
110
|
+
const report = { content: 'Test report' };
|
|
111
|
+
const result = exportReport(report, { format: 'html' });
|
|
112
|
+
expect(result.content).toContain('<html');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('exports as JSON', () => {
|
|
116
|
+
const report = { score: 85, gaps: [] };
|
|
117
|
+
const result = exportReport(report, { format: 'json' });
|
|
118
|
+
expect(JSON.parse(result.content)).toEqual(report);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('exports as markdown', () => {
|
|
122
|
+
const report = { content: 'Test report', title: 'Compliance Report' };
|
|
123
|
+
const result = exportReport(report, { format: 'markdown' });
|
|
124
|
+
expect(result.content).toContain('# Compliance Report');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|