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.
Files changed (170) hide show
  1. package/docker-compose.dev.yml +6 -3
  2. package/package.json +1 -1
  3. package/server/index.js +229 -14
  4. package/server/lib/compliance/control-mapper.js +401 -0
  5. package/server/lib/compliance/control-mapper.test.js +117 -0
  6. package/server/lib/compliance/evidence-linker.js +296 -0
  7. package/server/lib/compliance/evidence-linker.test.js +121 -0
  8. package/server/lib/compliance/gdpr-checklist.js +416 -0
  9. package/server/lib/compliance/gdpr-checklist.test.js +131 -0
  10. package/server/lib/compliance/hipaa-checklist.js +277 -0
  11. package/server/lib/compliance/hipaa-checklist.test.js +101 -0
  12. package/server/lib/compliance/iso27001-checklist.js +287 -0
  13. package/server/lib/compliance/iso27001-checklist.test.js +99 -0
  14. package/server/lib/compliance/multi-framework-reporter.js +284 -0
  15. package/server/lib/compliance/multi-framework-reporter.test.js +127 -0
  16. package/server/lib/compliance/pci-dss-checklist.js +214 -0
  17. package/server/lib/compliance/pci-dss-checklist.test.js +95 -0
  18. package/server/lib/compliance/trust-centre.js +187 -0
  19. package/server/lib/compliance/trust-centre.test.js +93 -0
  20. package/server/lib/dashboard/api-server.js +155 -0
  21. package/server/lib/dashboard/api-server.test.js +155 -0
  22. package/server/lib/dashboard/health-api.js +199 -0
  23. package/server/lib/dashboard/health-api.test.js +122 -0
  24. package/server/lib/dashboard/notes-api.js +234 -0
  25. package/server/lib/dashboard/notes-api.test.js +134 -0
  26. package/server/lib/dashboard/router-api.js +176 -0
  27. package/server/lib/dashboard/router-api.test.js +132 -0
  28. package/server/lib/dashboard/tasks-api.js +289 -0
  29. package/server/lib/dashboard/tasks-api.test.js +161 -0
  30. package/server/lib/dashboard/tlc-introspection.js +197 -0
  31. package/server/lib/dashboard/tlc-introspection.test.js +138 -0
  32. package/server/lib/dashboard/version-api.js +222 -0
  33. package/server/lib/dashboard/version-api.test.js +112 -0
  34. package/server/lib/dashboard/websocket-server.js +104 -0
  35. package/server/lib/dashboard/websocket-server.test.js +118 -0
  36. package/server/lib/deploy/branch-classifier.js +163 -0
  37. package/server/lib/deploy/branch-classifier.test.js +164 -0
  38. package/server/lib/deploy/deployment-approval.js +299 -0
  39. package/server/lib/deploy/deployment-approval.test.js +296 -0
  40. package/server/lib/deploy/deployment-audit.js +374 -0
  41. package/server/lib/deploy/deployment-audit.test.js +307 -0
  42. package/server/lib/deploy/deployment-executor.js +335 -0
  43. package/server/lib/deploy/deployment-executor.test.js +329 -0
  44. package/server/lib/deploy/deployment-rules.js +163 -0
  45. package/server/lib/deploy/deployment-rules.test.js +188 -0
  46. package/server/lib/deploy/rollback-manager.js +379 -0
  47. package/server/lib/deploy/rollback-manager.test.js +321 -0
  48. package/server/lib/deploy/security-gates.js +236 -0
  49. package/server/lib/deploy/security-gates.test.js +222 -0
  50. package/server/lib/k8s/gitops-config.js +188 -0
  51. package/server/lib/k8s/gitops-config.test.js +59 -0
  52. package/server/lib/k8s/helm-generator.js +196 -0
  53. package/server/lib/k8s/helm-generator.test.js +59 -0
  54. package/server/lib/k8s/kustomize-generator.js +176 -0
  55. package/server/lib/k8s/kustomize-generator.test.js +58 -0
  56. package/server/lib/k8s/network-policy.js +114 -0
  57. package/server/lib/k8s/network-policy.test.js +53 -0
  58. package/server/lib/k8s/pod-security.js +114 -0
  59. package/server/lib/k8s/pod-security.test.js +55 -0
  60. package/server/lib/k8s/rbac-generator.js +132 -0
  61. package/server/lib/k8s/rbac-generator.test.js +57 -0
  62. package/server/lib/k8s/resource-manager.js +172 -0
  63. package/server/lib/k8s/resource-manager.test.js +60 -0
  64. package/server/lib/k8s/secrets-encryption.js +168 -0
  65. package/server/lib/k8s/secrets-encryption.test.js +49 -0
  66. package/server/lib/monitoring/alert-manager.js +238 -0
  67. package/server/lib/monitoring/alert-manager.test.js +106 -0
  68. package/server/lib/monitoring/health-check.js +226 -0
  69. package/server/lib/monitoring/health-check.test.js +176 -0
  70. package/server/lib/monitoring/incident-manager.js +230 -0
  71. package/server/lib/monitoring/incident-manager.test.js +98 -0
  72. package/server/lib/monitoring/log-aggregator.js +147 -0
  73. package/server/lib/monitoring/log-aggregator.test.js +89 -0
  74. package/server/lib/monitoring/metrics-collector.js +337 -0
  75. package/server/lib/monitoring/metrics-collector.test.js +172 -0
  76. package/server/lib/monitoring/status-page.js +214 -0
  77. package/server/lib/monitoring/status-page.test.js +105 -0
  78. package/server/lib/monitoring/uptime-monitor.js +194 -0
  79. package/server/lib/monitoring/uptime-monitor.test.js +109 -0
  80. package/server/lib/network/fail2ban-config.js +294 -0
  81. package/server/lib/network/fail2ban-config.test.js +275 -0
  82. package/server/lib/network/firewall-manager.js +252 -0
  83. package/server/lib/network/firewall-manager.test.js +254 -0
  84. package/server/lib/network/geoip-filter.js +282 -0
  85. package/server/lib/network/geoip-filter.test.js +264 -0
  86. package/server/lib/network/rate-limiter.js +229 -0
  87. package/server/lib/network/rate-limiter.test.js +293 -0
  88. package/server/lib/network/request-validator.js +351 -0
  89. package/server/lib/network/request-validator.test.js +345 -0
  90. package/server/lib/network/security-headers.js +251 -0
  91. package/server/lib/network/security-headers.test.js +283 -0
  92. package/server/lib/network/tls-config.js +210 -0
  93. package/server/lib/network/tls-config.test.js +248 -0
  94. package/server/lib/security/auth-security.js +369 -0
  95. package/server/lib/security/auth-security.test.js +448 -0
  96. package/server/lib/security/cis-benchmark.js +152 -0
  97. package/server/lib/security/cis-benchmark.test.js +137 -0
  98. package/server/lib/security/compose-templates.js +312 -0
  99. package/server/lib/security/compose-templates.test.js +229 -0
  100. package/server/lib/security/container-runtime.js +456 -0
  101. package/server/lib/security/container-runtime.test.js +503 -0
  102. package/server/lib/security/cors-validator.js +278 -0
  103. package/server/lib/security/cors-validator.test.js +310 -0
  104. package/server/lib/security/crypto-utils.js +253 -0
  105. package/server/lib/security/crypto-utils.test.js +409 -0
  106. package/server/lib/security/dockerfile-linter.js +459 -0
  107. package/server/lib/security/dockerfile-linter.test.js +483 -0
  108. package/server/lib/security/dockerfile-templates.js +278 -0
  109. package/server/lib/security/dockerfile-templates.test.js +164 -0
  110. package/server/lib/security/error-sanitizer.js +426 -0
  111. package/server/lib/security/error-sanitizer.test.js +331 -0
  112. package/server/lib/security/headers-generator.js +368 -0
  113. package/server/lib/security/headers-generator.test.js +398 -0
  114. package/server/lib/security/image-scanner.js +83 -0
  115. package/server/lib/security/image-scanner.test.js +106 -0
  116. package/server/lib/security/input-validator.js +352 -0
  117. package/server/lib/security/input-validator.test.js +330 -0
  118. package/server/lib/security/network-policy.js +174 -0
  119. package/server/lib/security/network-policy.test.js +164 -0
  120. package/server/lib/security/output-encoder.js +237 -0
  121. package/server/lib/security/output-encoder.test.js +276 -0
  122. package/server/lib/security/path-validator.js +359 -0
  123. package/server/lib/security/path-validator.test.js +293 -0
  124. package/server/lib/security/query-builder.js +421 -0
  125. package/server/lib/security/query-builder.test.js +318 -0
  126. package/server/lib/security/secret-detector.js +290 -0
  127. package/server/lib/security/secret-detector.test.js +354 -0
  128. package/server/lib/security/secrets-validator.js +137 -0
  129. package/server/lib/security/secrets-validator.test.js +120 -0
  130. package/server/lib/security-testing/dast-runner.js +154 -0
  131. package/server/lib/security-testing/dast-runner.test.js +62 -0
  132. package/server/lib/security-testing/dependency-scanner.js +172 -0
  133. package/server/lib/security-testing/dependency-scanner.test.js +64 -0
  134. package/server/lib/security-testing/pentest-runner.js +230 -0
  135. package/server/lib/security-testing/pentest-runner.test.js +60 -0
  136. package/server/lib/security-testing/sast-runner.js +136 -0
  137. package/server/lib/security-testing/sast-runner.test.js +62 -0
  138. package/server/lib/security-testing/secret-scanner.js +153 -0
  139. package/server/lib/security-testing/secret-scanner.test.js +66 -0
  140. package/server/lib/security-testing/security-gate.js +216 -0
  141. package/server/lib/security-testing/security-gate.test.js +115 -0
  142. package/server/lib/security-testing/security-reporter.js +303 -0
  143. package/server/lib/security-testing/security-reporter.test.js +114 -0
  144. package/server/lib/standards/audit-checker.js +546 -0
  145. package/server/lib/standards/audit-checker.test.js +415 -0
  146. package/server/lib/standards/cleanup-executor.js +452 -0
  147. package/server/lib/standards/cleanup-executor.test.js +293 -0
  148. package/server/lib/standards/refactor-stepper.js +425 -0
  149. package/server/lib/standards/refactor-stepper.test.js +298 -0
  150. package/server/lib/standards/standards-injector.js +167 -0
  151. package/server/lib/standards/standards-injector.test.js +232 -0
  152. package/server/lib/user-management.test.js +284 -0
  153. package/server/lib/vps/backup-manager.js +157 -0
  154. package/server/lib/vps/backup-manager.test.js +59 -0
  155. package/server/lib/vps/caddy-config.js +159 -0
  156. package/server/lib/vps/caddy-config.test.js +48 -0
  157. package/server/lib/vps/compose-orchestrator.js +219 -0
  158. package/server/lib/vps/compose-orchestrator.test.js +50 -0
  159. package/server/lib/vps/database-config.js +208 -0
  160. package/server/lib/vps/database-config.test.js +47 -0
  161. package/server/lib/vps/deploy-script.js +211 -0
  162. package/server/lib/vps/deploy-script.test.js +53 -0
  163. package/server/lib/vps/secrets-manager.js +148 -0
  164. package/server/lib/vps/secrets-manager.test.js +58 -0
  165. package/server/lib/vps/server-hardening.js +174 -0
  166. package/server/lib/vps/server-hardening.test.js +70 -0
  167. package/server/package-lock.json +19 -0
  168. package/server/package.json +1 -0
  169. package/server/templates/CLAUDE.md +37 -0
  170. package/server/templates/CODING-STANDARDS.md +408 -0
@@ -0,0 +1,369 @@
1
+ /**
2
+ * Auth Security Module
3
+ *
4
+ * Secure authentication primitives.
5
+ * Addresses OWASP A07: Identification and Authentication Failures
6
+ */
7
+
8
+ import crypto from 'crypto';
9
+
10
+ /**
11
+ * Argon2id parameters (OWASP recommended)
12
+ */
13
+ const ARGON2_CONFIG = {
14
+ type: 2, // argon2id
15
+ memoryCost: 65536, // 64 MB
16
+ timeCost: 3,
17
+ parallelism: 4,
18
+ hashLength: 32,
19
+ saltLength: 16,
20
+ };
21
+
22
+ /**
23
+ * Hash a password using Argon2id
24
+ * Falls back to scrypt for compatibility if argon2 is not available
25
+ * @param {string} password - Password to hash
26
+ * @returns {Promise<string>} Hashed password
27
+ */
28
+ export async function hashPassword(password) {
29
+ if (!password || password.length === 0) {
30
+ throw new Error('Password cannot be empty');
31
+ }
32
+
33
+ // Generate random salt
34
+ const salt = crypto.randomBytes(ARGON2_CONFIG.saltLength);
35
+
36
+ // Use scrypt as a fallback (similar security, widely available)
37
+ // In production, you'd use argon2 library
38
+ return new Promise((resolve, reject) => {
39
+ const keyLength = ARGON2_CONFIG.hashLength;
40
+ const options = {
41
+ N: 16384, // CPU/memory cost parameter
42
+ r: 8, // Block size
43
+ p: 1, // Parallelization
44
+ maxmem: ARGON2_CONFIG.memoryCost * 1024,
45
+ };
46
+
47
+ crypto.scrypt(password, salt, keyLength, options, (err, derivedKey) => {
48
+ if (err) {
49
+ reject(err);
50
+ return;
51
+ }
52
+
53
+ // Format as Argon2id-like hash for future migration
54
+ const params = `m=${ARGON2_CONFIG.memoryCost},t=${ARGON2_CONFIG.timeCost},p=${ARGON2_CONFIG.parallelism}`;
55
+ const saltB64 = salt.toString('base64').replace(/=/g, '');
56
+ const hashB64 = derivedKey.toString('base64').replace(/=/g, '');
57
+
58
+ resolve(`$argon2id$v=19$${params}$${saltB64}$${hashB64}`);
59
+ });
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Verify a password against a hash
65
+ * @param {string} password - Password to verify
66
+ * @param {string} hash - Hash to verify against
67
+ * @returns {Promise<boolean>} True if password matches
68
+ */
69
+ export async function verifyPassword(password, hash) {
70
+ if (!password || password.length === 0) {
71
+ return false;
72
+ }
73
+
74
+ if (!hash || !hash.startsWith('$argon2id$')) {
75
+ return false;
76
+ }
77
+
78
+ try {
79
+ // Parse the hash
80
+ const parts = hash.split('$');
81
+ if (parts.length < 6) {
82
+ return false;
83
+ }
84
+
85
+ // Extract salt and hash
86
+ const saltB64 = parts[4];
87
+ const hashB64 = parts[5];
88
+
89
+ // Decode salt (add padding if needed)
90
+ const saltPadded = saltB64 + '='.repeat((4 - (saltB64.length % 4)) % 4);
91
+ const salt = Buffer.from(saltPadded, 'base64');
92
+
93
+ // Decode expected hash
94
+ const hashPadded = hashB64 + '='.repeat((4 - (hashB64.length % 4)) % 4);
95
+ const expectedHash = Buffer.from(hashPadded, 'base64');
96
+
97
+ // Compute hash of input password
98
+ return new Promise((resolve) => {
99
+ const options = {
100
+ N: 16384,
101
+ r: 8,
102
+ p: 1,
103
+ maxmem: ARGON2_CONFIG.memoryCost * 1024,
104
+ };
105
+
106
+ crypto.scrypt(password, salt, expectedHash.length, options, (err, derivedKey) => {
107
+ if (err) {
108
+ resolve(false);
109
+ return;
110
+ }
111
+
112
+ // Timing-safe comparison
113
+ resolve(crypto.timingSafeEqual(derivedKey, expectedHash));
114
+ });
115
+ });
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Generate a cryptographically secure session token
123
+ * @param {Object} options - Token options
124
+ * @returns {string} Session token
125
+ */
126
+ export function generateSessionToken(options = {}) {
127
+ const { bytes = 32, encoding = 'hex' } = options;
128
+
129
+ const buffer = crypto.randomBytes(bytes);
130
+
131
+ if (encoding === 'base64url') {
132
+ return buffer.toString('base64url');
133
+ }
134
+
135
+ return buffer.toString('hex');
136
+ }
137
+
138
+ /**
139
+ * Validate session token format
140
+ * @param {string} token - Token to validate
141
+ * @param {Object} options - Validation options
142
+ * @returns {Object} Validation result
143
+ */
144
+ export function validateSessionToken(token, options = {}) {
145
+ const { expectedLength = 64 } = options;
146
+
147
+ if (!token || token.length === 0) {
148
+ return {
149
+ valid: false,
150
+ reason: 'Token is empty',
151
+ };
152
+ }
153
+
154
+ if (token.length !== expectedLength) {
155
+ return {
156
+ valid: false,
157
+ reason: `Token length must be ${expectedLength} characters`,
158
+ };
159
+ }
160
+
161
+ if (!/^[a-f0-9]+$/i.test(token)) {
162
+ return {
163
+ valid: false,
164
+ reason: 'Token contains invalid characters',
165
+ };
166
+ }
167
+
168
+ return { valid: true };
169
+ }
170
+
171
+ /**
172
+ * Create a rate limiter
173
+ * @param {Object} options - Rate limiter options
174
+ * @returns {Object} Rate limiter instance
175
+ */
176
+ export function createRateLimiter(options = {}) {
177
+ const { maxAttempts = 5, windowMs = 60000 } = options;
178
+
179
+ const attempts = new Map();
180
+
181
+ return {
182
+ /**
183
+ * Check if request is allowed
184
+ * @param {string} key - Identifier (e.g., email, IP)
185
+ * @returns {Object} Rate limit result
186
+ */
187
+ check(key) {
188
+ const now = Date.now();
189
+ const record = attempts.get(key);
190
+
191
+ if (!record || now - record.windowStart >= windowMs) {
192
+ // New window
193
+ attempts.set(key, {
194
+ count: 1,
195
+ windowStart: now,
196
+ });
197
+ return {
198
+ allowed: true,
199
+ remaining: maxAttempts - 1,
200
+ retryAfter: 0,
201
+ };
202
+ }
203
+
204
+ if (record.count >= maxAttempts) {
205
+ const retryAfter = Math.ceil((record.windowStart + windowMs - now) / 1000);
206
+ return {
207
+ allowed: false,
208
+ remaining: 0,
209
+ retryAfter,
210
+ };
211
+ }
212
+
213
+ record.count++;
214
+ return {
215
+ allowed: true,
216
+ remaining: maxAttempts - record.count,
217
+ retryAfter: 0,
218
+ };
219
+ },
220
+
221
+ /**
222
+ * Reset rate limit for a key
223
+ * @param {string} key - Identifier to reset
224
+ */
225
+ reset(key) {
226
+ attempts.delete(key);
227
+ },
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Create an account lockout manager
233
+ * @param {Object} options - Lockout options
234
+ * @returns {Object} Account lockout instance
235
+ */
236
+ export function createAccountLockout(options = {}) {
237
+ const {
238
+ maxFailures = 5,
239
+ lockoutDurationMs = 900000, // 15 minutes
240
+ exponentialBackoff = false,
241
+ } = options;
242
+
243
+ const accounts = new Map();
244
+
245
+ return {
246
+ /**
247
+ * Record a failed login attempt
248
+ * @param {string} accountId - Account identifier
249
+ */
250
+ recordFailure(accountId) {
251
+ const now = Date.now();
252
+ let record = accounts.get(accountId);
253
+
254
+ if (!record) {
255
+ record = {
256
+ failures: 0,
257
+ lockoutCount: 0,
258
+ lockedUntil: null,
259
+ };
260
+ accounts.set(accountId, record);
261
+ }
262
+
263
+ record.failures++;
264
+
265
+ if (record.failures >= maxFailures) {
266
+ // Lock the account
267
+ const multiplier = exponentialBackoff ? Math.pow(2, record.lockoutCount) : 1;
268
+ record.lockedUntil = now + lockoutDurationMs * multiplier;
269
+ record.lockoutCount++;
270
+ }
271
+ },
272
+
273
+ /**
274
+ * Record a successful login
275
+ * @param {string} accountId - Account identifier
276
+ */
277
+ recordSuccess(accountId) {
278
+ const record = accounts.get(accountId);
279
+ if (record) {
280
+ record.failures = 0;
281
+ record.lockedUntil = null;
282
+ }
283
+ },
284
+
285
+ /**
286
+ * Check if account is locked
287
+ * @param {string} accountId - Account identifier
288
+ * @returns {Object} Lock status
289
+ */
290
+ isLocked(accountId) {
291
+ const now = Date.now();
292
+ const record = accounts.get(accountId);
293
+
294
+ if (!record || !record.lockedUntil) {
295
+ return { locked: false };
296
+ }
297
+
298
+ if (now >= record.lockedUntil) {
299
+ // Lockout expired, reset failures but keep lockout count for backoff
300
+ record.failures = 0;
301
+ record.lockedUntil = null;
302
+ return { locked: false };
303
+ }
304
+
305
+ return {
306
+ locked: true,
307
+ unlockAt: record.lockedUntil,
308
+ };
309
+ },
310
+ };
311
+ }
312
+
313
+ /**
314
+ * Generate secure cookie options
315
+ * @param {Object} options - Cookie options
316
+ * @returns {Object} Cookie configuration
317
+ */
318
+ export function generateCookieOptions(options = {}) {
319
+ const {
320
+ production = false,
321
+ sameSite = 'Strict',
322
+ maxAge,
323
+ domain,
324
+ path = '/',
325
+ } = options;
326
+
327
+ const cookieOptions = {
328
+ httpOnly: true,
329
+ secure: production,
330
+ sameSite,
331
+ path,
332
+ };
333
+
334
+ if (maxAge !== undefined) {
335
+ cookieOptions.maxAge = maxAge;
336
+ }
337
+
338
+ if (domain) {
339
+ cookieOptions.domain = domain;
340
+ }
341
+
342
+ return cookieOptions;
343
+ }
344
+
345
+ /**
346
+ * Timing-safe string comparison
347
+ * @param {string} a - First string
348
+ * @param {string} b - Second string
349
+ * @returns {boolean} True if strings are equal
350
+ */
351
+ export function timingSafeCompare(a, b) {
352
+ if (typeof a !== 'string' || typeof b !== 'string') {
353
+ return false;
354
+ }
355
+
356
+ // Use constant-time comparison
357
+ const bufA = Buffer.from(a);
358
+ const bufB = Buffer.from(b);
359
+
360
+ // If lengths differ, compare against dummy buffer of same length
361
+ // to avoid timing leak from length difference
362
+ if (bufA.length !== bufB.length) {
363
+ // Do a dummy comparison to maintain constant time
364
+ crypto.timingSafeEqual(bufA, bufA);
365
+ return false;
366
+ }
367
+
368
+ return crypto.timingSafeEqual(bufA, bufB);
369
+ }