tlc-claude-code 1.4.7 → 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/docker-compose.dev.yml +6 -3
- 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,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GDPR Checklist - GDPR articles checklist for compliance
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// GDPR Articles data
|
|
6
|
+
const GDPR_ARTICLES = [
|
|
7
|
+
{
|
|
8
|
+
number: 5,
|
|
9
|
+
chapter: 2,
|
|
10
|
+
title: 'Principles relating to processing of personal data',
|
|
11
|
+
requirements: [
|
|
12
|
+
'lawfulness, fairness and transparency',
|
|
13
|
+
'purpose limitation',
|
|
14
|
+
'data minimization',
|
|
15
|
+
'accuracy',
|
|
16
|
+
'storage limitation',
|
|
17
|
+
'integrity and confidentiality',
|
|
18
|
+
'accountability'
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
number: 6,
|
|
23
|
+
chapter: 2,
|
|
24
|
+
title: 'Lawfulness of processing',
|
|
25
|
+
requirements: [
|
|
26
|
+
'consent',
|
|
27
|
+
'contract',
|
|
28
|
+
'legal obligation',
|
|
29
|
+
'vital interests',
|
|
30
|
+
'public task',
|
|
31
|
+
'legitimate interests'
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
number: 7,
|
|
36
|
+
chapter: 2,
|
|
37
|
+
title: 'Conditions for consent',
|
|
38
|
+
requirements: ['demonstrable consent', 'clear and plain language', 'right to withdraw']
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
number: 12,
|
|
42
|
+
chapter: 3,
|
|
43
|
+
title: 'Transparent information',
|
|
44
|
+
requirements: ['concise', 'transparent', 'intelligible', 'easily accessible']
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
number: 13,
|
|
48
|
+
chapter: 3,
|
|
49
|
+
title: 'Information to be provided',
|
|
50
|
+
requirements: ['controller identity', 'purposes', 'recipients', 'retention period', 'rights']
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
number: 15,
|
|
54
|
+
chapter: 3,
|
|
55
|
+
title: 'Right of access',
|
|
56
|
+
requirements: ['confirmation of processing', 'copy of data', 'supplementary information']
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
number: 16,
|
|
60
|
+
chapter: 3,
|
|
61
|
+
title: 'Right to rectification',
|
|
62
|
+
requirements: ['correct inaccurate data', 'complete incomplete data']
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
number: 17,
|
|
66
|
+
chapter: 3,
|
|
67
|
+
title: 'Right to erasure',
|
|
68
|
+
requirements: ['erasure without undue delay', 'grounds for erasure']
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
number: 18,
|
|
72
|
+
chapter: 3,
|
|
73
|
+
title: 'Right to restriction of processing',
|
|
74
|
+
requirements: ['restrict processing in certain circumstances']
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
number: 20,
|
|
78
|
+
chapter: 3,
|
|
79
|
+
title: 'Right to data portability',
|
|
80
|
+
requirements: ['receive data in structured format', 'transmit to another controller']
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
number: 25,
|
|
84
|
+
chapter: 4,
|
|
85
|
+
title: 'Data protection by design and default',
|
|
86
|
+
requirements: ['appropriate technical measures', 'data minimization by default']
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
number: 30,
|
|
90
|
+
chapter: 4,
|
|
91
|
+
title: 'Records of processing activities',
|
|
92
|
+
requirements: ['maintain records', 'document processing activities']
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
number: 32,
|
|
96
|
+
chapter: 4,
|
|
97
|
+
title: 'Security of processing',
|
|
98
|
+
requirements: ['pseudonymisation', 'encryption', 'confidentiality', 'integrity', 'availability', 'resilience']
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
number: 33,
|
|
102
|
+
chapter: 4,
|
|
103
|
+
title: 'Notification of personal data breach',
|
|
104
|
+
requirements: ['notify within 72 hours', 'document breaches', 'describe nature of breach']
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
number: 34,
|
|
108
|
+
chapter: 4,
|
|
109
|
+
title: 'Communication of personal data breach to data subject',
|
|
110
|
+
requirements: ['notify data subjects of high risk breaches']
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
number: 35,
|
|
114
|
+
chapter: 4,
|
|
115
|
+
title: 'Data protection impact assessment',
|
|
116
|
+
requirements: ['assess impact of high-risk processing', 'seek views of data subjects']
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
number: 37,
|
|
120
|
+
chapter: 4,
|
|
121
|
+
title: 'Designation of data protection officer',
|
|
122
|
+
requirements: ['appoint DPO when required', 'ensure expertise']
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
number: 44,
|
|
126
|
+
chapter: 5,
|
|
127
|
+
title: 'General principle for transfers',
|
|
128
|
+
requirements: ['adequate safeguards for international transfers']
|
|
129
|
+
}
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
const GDPR_PRINCIPLES = [
|
|
133
|
+
'lawfulness',
|
|
134
|
+
'purpose-limitation',
|
|
135
|
+
'data-minimization',
|
|
136
|
+
'accuracy',
|
|
137
|
+
'storage-limitation',
|
|
138
|
+
'integrity-confidentiality'
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
// Special category data patterns
|
|
142
|
+
const SPECIAL_CATEGORY_PATTERNS = [
|
|
143
|
+
/health/i,
|
|
144
|
+
/medical/i,
|
|
145
|
+
/genetic/i,
|
|
146
|
+
/biometric/i,
|
|
147
|
+
/racial/i,
|
|
148
|
+
/ethnic/i,
|
|
149
|
+
/political/i,
|
|
150
|
+
/religious/i,
|
|
151
|
+
/sexual/i,
|
|
152
|
+
/trade.?union/i
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
// Personal data patterns
|
|
156
|
+
const PERSONAL_DATA_PATTERNS = [
|
|
157
|
+
/email/i,
|
|
158
|
+
/name/i,
|
|
159
|
+
/address/i,
|
|
160
|
+
/phone/i,
|
|
161
|
+
/ip.?address/i,
|
|
162
|
+
/location/i,
|
|
163
|
+
/user/i,
|
|
164
|
+
/customer/i,
|
|
165
|
+
/employee/i
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create a GDPR checklist instance
|
|
170
|
+
*/
|
|
171
|
+
export function createGdprChecklist() {
|
|
172
|
+
return {
|
|
173
|
+
evaluate: async (evidence) => {
|
|
174
|
+
let compliantCount = 0;
|
|
175
|
+
const totalArticles = GDPR_ARTICLES.length;
|
|
176
|
+
const gaps = [];
|
|
177
|
+
|
|
178
|
+
for (const article of GDPR_ARTICLES) {
|
|
179
|
+
const result = checkArticle(article.number, evidence || {});
|
|
180
|
+
if (result.compliant) {
|
|
181
|
+
compliantCount++;
|
|
182
|
+
} else {
|
|
183
|
+
gaps.push({ article: article.number, issues: result.issues });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
score: Math.round((compliantCount / totalArticles) * 100),
|
|
189
|
+
gaps
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
getArticles: () => GDPR_ARTICLES,
|
|
193
|
+
getPrinciples: () => GDPR_PRINCIPLES
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get GDPR articles, optionally filtered
|
|
199
|
+
*/
|
|
200
|
+
export function getArticles(options = {}) {
|
|
201
|
+
let articles = [...GDPR_ARTICLES];
|
|
202
|
+
|
|
203
|
+
if (options.chapter !== undefined) {
|
|
204
|
+
articles = articles.filter(a => a.chapter === options.chapter);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return articles;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Check compliance for a specific article
|
|
212
|
+
*/
|
|
213
|
+
export function checkArticle(articleNumber, evidence = {}) {
|
|
214
|
+
const article = GDPR_ARTICLES.find(a => a.number === articleNumber);
|
|
215
|
+
|
|
216
|
+
const result = {
|
|
217
|
+
articleNumber,
|
|
218
|
+
compliant: false,
|
|
219
|
+
issues: []
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
if (!article) {
|
|
223
|
+
result.issues.push(`Unknown article: ${articleNumber}`);
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Article-specific compliance checks
|
|
228
|
+
switch (articleNumber) {
|
|
229
|
+
case 5: // Principles
|
|
230
|
+
result.compliant = evidence.principles !== false;
|
|
231
|
+
break;
|
|
232
|
+
case 6: // Lawful basis
|
|
233
|
+
result.compliant = !!(evidence.lawfulBasis && evidence.consentRecords);
|
|
234
|
+
if (!evidence.lawfulBasis) result.issues.push('No lawful basis documented');
|
|
235
|
+
if (!evidence.consentRecords) result.issues.push('No consent records');
|
|
236
|
+
break;
|
|
237
|
+
case 15: // Right of access and other data subject rights
|
|
238
|
+
case 16:
|
|
239
|
+
case 17:
|
|
240
|
+
case 18:
|
|
241
|
+
case 20:
|
|
242
|
+
result.compliant = !!(
|
|
243
|
+
evidence.rightToAccess &&
|
|
244
|
+
evidence.rightToRectification &&
|
|
245
|
+
evidence.rightToErasure &&
|
|
246
|
+
evidence.rightToPortability
|
|
247
|
+
);
|
|
248
|
+
break;
|
|
249
|
+
case 33: // Breach notification
|
|
250
|
+
result.compliant = !!(evidence.breachProcedure && evidence.notificationTemplate);
|
|
251
|
+
if (!evidence.breachProcedure) result.issues.push('No breach procedure');
|
|
252
|
+
if (!evidence.notificationTemplate) result.issues.push('No notification template');
|
|
253
|
+
break;
|
|
254
|
+
default:
|
|
255
|
+
// For other articles, assume compliant if any evidence provided
|
|
256
|
+
result.compliant = Object.keys(evidence).length > 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Generate a Data Protection Impact Assessment
|
|
264
|
+
*/
|
|
265
|
+
export function generateDpia(processing) {
|
|
266
|
+
const riskLevel = assessRiskLevel(processing);
|
|
267
|
+
|
|
268
|
+
let dpia = `# Data Protection Impact Assessment\n\n`;
|
|
269
|
+
dpia += `## Processing Activity\n\n`;
|
|
270
|
+
dpia += `**Purpose:** ${processing.purpose || 'Not specified'}\n\n`;
|
|
271
|
+
|
|
272
|
+
if (processing.dataTypes) {
|
|
273
|
+
dpia += `**Data Types:** ${processing.dataTypes.join(', ')}\n\n`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (processing.recipients) {
|
|
277
|
+
dpia += `**Recipients:** ${processing.recipients.join(', ')}\n\n`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (processing.retention) {
|
|
281
|
+
dpia += `**Retention Period:** ${processing.retention}\n\n`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
dpia += `## Risk Assessment\n\n`;
|
|
285
|
+
dpia += `**Risk Level:** ${riskLevel}\n\n`;
|
|
286
|
+
|
|
287
|
+
if (processing.specialCategories) {
|
|
288
|
+
dpia += `**Note:** This processing involves special category data requiring additional safeguards.\n\n`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
dpia += `## Necessity and Proportionality\n\n`;
|
|
292
|
+
dpia += `[To be completed]\n\n`;
|
|
293
|
+
|
|
294
|
+
dpia += `## Measures to Address Risks\n\n`;
|
|
295
|
+
dpia += `[To be completed]\n`;
|
|
296
|
+
|
|
297
|
+
return dpia;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Assess risk level for DPIA
|
|
302
|
+
*/
|
|
303
|
+
function assessRiskLevel(processing) {
|
|
304
|
+
if (processing.specialCategories) {
|
|
305
|
+
return 'HIGH';
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (processing.dataTypes) {
|
|
309
|
+
const hasSpecialData = processing.dataTypes.some(dt =>
|
|
310
|
+
SPECIAL_CATEGORY_PATTERNS.some(p => p.test(dt))
|
|
311
|
+
);
|
|
312
|
+
if (hasSpecialData) {
|
|
313
|
+
return 'HIGH';
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (processing.largeScale || processing.systematicMonitoring) {
|
|
318
|
+
return 'HIGH';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return 'MEDIUM';
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Assess data processing patterns in code
|
|
326
|
+
*/
|
|
327
|
+
export function assessDataProcessing(codePatterns) {
|
|
328
|
+
const result = {
|
|
329
|
+
personalDataIdentified: false,
|
|
330
|
+
specialCategories: false,
|
|
331
|
+
patterns: []
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
for (const pattern of codePatterns) {
|
|
335
|
+
const patternStr = pattern.pattern || '';
|
|
336
|
+
|
|
337
|
+
// Check for personal data
|
|
338
|
+
if (PERSONAL_DATA_PATTERNS.some(p => p.test(patternStr))) {
|
|
339
|
+
result.personalDataIdentified = true;
|
|
340
|
+
result.patterns.push({
|
|
341
|
+
file: pattern.file,
|
|
342
|
+
type: 'personal-data',
|
|
343
|
+
pattern: patternStr
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Check for special categories
|
|
348
|
+
if (SPECIAL_CATEGORY_PATTERNS.some(p => p.test(patternStr))) {
|
|
349
|
+
result.specialCategories = true;
|
|
350
|
+
result.personalDataIdentified = true;
|
|
351
|
+
result.patterns.push({
|
|
352
|
+
file: pattern.file,
|
|
353
|
+
type: 'special-category',
|
|
354
|
+
pattern: patternStr
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return result;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Generate a privacy notice template
|
|
364
|
+
*/
|
|
365
|
+
export function generatePrivacyNotice(config = {}) {
|
|
366
|
+
let notice = `# Privacy Notice\n\n`;
|
|
367
|
+
|
|
368
|
+
notice += `## Who we are\n\n`;
|
|
369
|
+
notice += `The data controller for your personal data is ${config.controller || '[Controller Name]'}.\n\n`;
|
|
370
|
+
|
|
371
|
+
notice += `## What data we collect\n\n`;
|
|
372
|
+
notice += `We collect and process the following personal data:\n`;
|
|
373
|
+
notice += `[List of data categories]\n\n`;
|
|
374
|
+
|
|
375
|
+
notice += `## Why we process your data\n\n`;
|
|
376
|
+
if (config.purposes && config.purposes.length > 0) {
|
|
377
|
+
notice += `We process your data for the following purposes:\n`;
|
|
378
|
+
config.purposes.forEach(p => {
|
|
379
|
+
notice += `- ${p}\n`;
|
|
380
|
+
});
|
|
381
|
+
} else {
|
|
382
|
+
notice += `[Processing purposes]\n`;
|
|
383
|
+
}
|
|
384
|
+
notice += `\n`;
|
|
385
|
+
|
|
386
|
+
notice += `## Legal basis for processing\n\n`;
|
|
387
|
+
notice += `Our lawful basis for processing is: ${config.lawfulBasis || '[Lawful basis]'}\n\n`;
|
|
388
|
+
|
|
389
|
+
notice += `## Data retention\n\n`;
|
|
390
|
+
notice += `We keep your data for the period necessary to fulfill the purposes for which it was collected. `;
|
|
391
|
+
notice += `Our retention policy ensures data is not kept longer than necessary.\n\n`;
|
|
392
|
+
|
|
393
|
+
notice += `## Your rights\n\n`;
|
|
394
|
+
notice += `Under data protection law, you have the following rights (your rights as a data subject):\n`;
|
|
395
|
+
notice += `- Right of access to your data\n`;
|
|
396
|
+
notice += `- Right to rectification of inaccurate data\n`;
|
|
397
|
+
notice += `- Right to erasure (right to be forgotten)\n`;
|
|
398
|
+
notice += `- Right to restrict processing\n`;
|
|
399
|
+
notice += `- Right to data portability\n`;
|
|
400
|
+
notice += `- Right to object to processing\n`;
|
|
401
|
+
notice += `- Rights related to automated decision making\n\n`;
|
|
402
|
+
|
|
403
|
+
notice += `## Contact us\n\n`;
|
|
404
|
+
notice += `If you have any questions about this privacy notice or how we handle your data, please contact us.\n`;
|
|
405
|
+
|
|
406
|
+
return notice;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export default {
|
|
410
|
+
createGdprChecklist,
|
|
411
|
+
getArticles,
|
|
412
|
+
checkArticle,
|
|
413
|
+
generateDpia,
|
|
414
|
+
assessDataProcessing,
|
|
415
|
+
generatePrivacyNotice
|
|
416
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GDPR Checklist Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
+
import { createGdprChecklist, getArticles, checkArticle, generateDpia, assessDataProcessing, generatePrivacyNotice } from './gdpr-checklist.js';
|
|
6
|
+
|
|
7
|
+
describe('gdpr-checklist', () => {
|
|
8
|
+
describe('createGdprChecklist', () => {
|
|
9
|
+
it('creates GDPR checklist', () => {
|
|
10
|
+
const checklist = createGdprChecklist();
|
|
11
|
+
expect(checklist.evaluate).toBeDefined();
|
|
12
|
+
expect(checklist.getArticles).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('covers key GDPR principles', () => {
|
|
16
|
+
const checklist = createGdprChecklist();
|
|
17
|
+
const principles = checklist.getPrinciples();
|
|
18
|
+
expect(principles).toContain('lawfulness');
|
|
19
|
+
expect(principles).toContain('purpose-limitation');
|
|
20
|
+
expect(principles).toContain('data-minimization');
|
|
21
|
+
expect(principles).toContain('accuracy');
|
|
22
|
+
expect(principles).toContain('storage-limitation');
|
|
23
|
+
expect(principles).toContain('integrity-confidentiality');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('getArticles', () => {
|
|
28
|
+
it('returns relevant GDPR articles', () => {
|
|
29
|
+
const articles = getArticles();
|
|
30
|
+
expect(articles.length).toBeGreaterThan(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('filters by chapter', () => {
|
|
34
|
+
const chapter2 = getArticles({ chapter: 2 });
|
|
35
|
+
expect(chapter2.every(a => a.chapter === 2)).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('includes article requirements', () => {
|
|
39
|
+
const articles = getArticles();
|
|
40
|
+
const art5 = articles.find(a => a.number === 5);
|
|
41
|
+
expect(art5.requirements).toBeDefined();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('checkArticle', () => {
|
|
46
|
+
it('checks article compliance', () => {
|
|
47
|
+
const evidence = { lawfulBasis: 'consent', consentRecords: true };
|
|
48
|
+
const result = checkArticle(6, evidence);
|
|
49
|
+
expect(result.articleNumber).toBe(6);
|
|
50
|
+
expect(result.compliant).toBeDefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('validates data subject rights', () => {
|
|
54
|
+
const evidence = {
|
|
55
|
+
rightToAccess: true,
|
|
56
|
+
rightToRectification: true,
|
|
57
|
+
rightToErasure: true,
|
|
58
|
+
rightToPortability: true
|
|
59
|
+
};
|
|
60
|
+
const result = checkArticle(15, evidence);
|
|
61
|
+
expect(result.compliant).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('checks breach notification readiness', () => {
|
|
65
|
+
const evidence = { breachProcedure: true, notificationTemplate: true };
|
|
66
|
+
const result = checkArticle(33, evidence);
|
|
67
|
+
expect(result.compliant).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('generateDpia', () => {
|
|
72
|
+
it('generates Data Protection Impact Assessment', () => {
|
|
73
|
+
const processing = {
|
|
74
|
+
purpose: 'User analytics',
|
|
75
|
+
dataTypes: ['browsing behavior', 'device info'],
|
|
76
|
+
recipients: ['analytics provider'],
|
|
77
|
+
retention: '2 years'
|
|
78
|
+
};
|
|
79
|
+
const dpia = generateDpia(processing);
|
|
80
|
+
expect(dpia).toContain('Data Protection Impact Assessment');
|
|
81
|
+
expect(dpia).toContain('User analytics');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('assesses risk level', () => {
|
|
85
|
+
const processing = {
|
|
86
|
+
purpose: 'Health monitoring',
|
|
87
|
+
dataTypes: ['health data'],
|
|
88
|
+
specialCategories: true
|
|
89
|
+
};
|
|
90
|
+
const dpia = generateDpia(processing);
|
|
91
|
+
expect(dpia).toContain('HIGH');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('assessDataProcessing', () => {
|
|
96
|
+
it('identifies personal data processing', () => {
|
|
97
|
+
const codePatterns = [
|
|
98
|
+
{ file: 'user.js', pattern: 'user.email' },
|
|
99
|
+
{ file: 'analytics.js', pattern: 'ipAddress' }
|
|
100
|
+
];
|
|
101
|
+
const assessment = assessDataProcessing(codePatterns);
|
|
102
|
+
expect(assessment.personalDataIdentified).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('classifies data categories', () => {
|
|
106
|
+
const codePatterns = [{ file: 'test.js', pattern: 'healthRecords' }];
|
|
107
|
+
const assessment = assessDataProcessing(codePatterns);
|
|
108
|
+
expect(assessment.specialCategories).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('generatePrivacyNotice', () => {
|
|
113
|
+
it('generates privacy notice template', () => {
|
|
114
|
+
const config = {
|
|
115
|
+
controller: 'TLC Inc',
|
|
116
|
+
purposes: ['Service delivery', 'Analytics'],
|
|
117
|
+
lawfulBasis: 'contract'
|
|
118
|
+
};
|
|
119
|
+
const notice = generatePrivacyNotice(config);
|
|
120
|
+
expect(notice).toContain('Privacy Notice');
|
|
121
|
+
expect(notice).toContain('TLC Inc');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('includes required information', () => {
|
|
125
|
+
const notice = generatePrivacyNotice({});
|
|
126
|
+
expect(notice).toContain('data controller');
|
|
127
|
+
expect(notice).toContain('your rights');
|
|
128
|
+
expect(notice).toContain('retention');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|