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,278 @@
1
+ /**
2
+ * Hardened Dockerfile Templates
3
+ *
4
+ * CIS Docker Benchmark compliant templates for production deployments.
5
+ */
6
+
7
+ export const SECURITY_HEADERS = {
8
+ nginx: `
9
+ # Security headers
10
+ add_header X-Content-Type-Options "nosniff" always;
11
+ add_header X-Frame-Options "SAMEORIGIN" always;
12
+ add_header X-XSS-Protection "1; mode=block" always;
13
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
14
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" always;
15
+ `,
16
+ express: `
17
+ // Security headers middleware
18
+ app.use((req, res, next) => {
19
+ res.setHeader('X-Content-Type-Options', 'nosniff');
20
+ res.setHeader('X-Frame-Options', 'SAMEORIGIN');
21
+ res.setHeader('X-XSS-Protection', '1; mode=block');
22
+ res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
23
+ next();
24
+ });
25
+ `,
26
+ };
27
+
28
+ /**
29
+ * Generate hardened Node.js server Dockerfile
30
+ */
31
+ export function generateServerDockerfile(options = {}) {
32
+ const {
33
+ nodeVersion = '20',
34
+ port = 5001,
35
+ appDir = '/app',
36
+ user = 'node',
37
+ maintainer = '',
38
+ } = options;
39
+
40
+ const labelSection = maintainer ? `LABEL maintainer="${maintainer}"\n` : 'LABEL maintainer="team@example.com"\n';
41
+
42
+ return `# Stage 1: Build
43
+ FROM node:${nodeVersion}-alpine AS builder
44
+
45
+ WORKDIR ${appDir}
46
+
47
+ # Install build dependencies
48
+ RUN apk add --no-cache python3 make g++
49
+
50
+ # Copy package files first for layer caching
51
+ COPY package*.json ./
52
+
53
+ # Install all dependencies (including devDependencies for build)
54
+ RUN npm ci --ignore-scripts
55
+
56
+ # Copy source code
57
+ COPY . .
58
+
59
+ # Build if needed (TypeScript, etc.)
60
+ RUN npm run build --if-present
61
+
62
+ # Remove devDependencies
63
+ RUN npm prune --production
64
+
65
+ # Stage 2: Production
66
+ FROM node:${nodeVersion}-alpine AS production
67
+
68
+ ${labelSection}
69
+ # Install dumb-init for proper signal handling
70
+ RUN apk add --no-cache dumb-init
71
+
72
+ # Set non-root user
73
+ USER ${user}
74
+
75
+ WORKDIR ${appDir}
76
+
77
+ # Copy built application from builder
78
+ COPY --from=builder --chown=${user}:${user} ${appDir}/node_modules ./node_modules
79
+ COPY --from=builder --chown=${user}:${user} ${appDir}/package*.json ./
80
+ COPY --from=builder --chown=${user}:${user} ${appDir}/dist ./dist
81
+ COPY --from=builder --chown=${user}:${user} ${appDir}/lib ./lib
82
+
83
+ # Set production environment
84
+ ENV NODE_ENV=production
85
+ ENV PORT=${port}
86
+
87
+ EXPOSE ${port}
88
+
89
+ # Health check
90
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\
91
+ CMD node -e "require('http').get('http://localhost:${port}/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
92
+
93
+ # Use dumb-init as entrypoint
94
+ ENTRYPOINT ["dumb-init", "--"]
95
+ CMD ["node", "lib/index.js"]
96
+ `;
97
+ }
98
+
99
+ /**
100
+ * Generate hardened React/Vite dashboard Dockerfile with nginx
101
+ */
102
+ export function generateDashboardDockerfile(options = {}) {
103
+ const {
104
+ nodeVersion = '20',
105
+ nginxVersion = '1.25',
106
+ port = 80,
107
+ buildDir = 'dist',
108
+ user = 'nginx',
109
+ maintainer = '',
110
+ } = options;
111
+
112
+ const labelSection = maintainer ? `LABEL maintainer="${maintainer}"\n` : 'LABEL maintainer="team@example.com"\n';
113
+
114
+ return `# Stage 1: Build
115
+ FROM node:${nodeVersion}-alpine AS builder
116
+
117
+ WORKDIR /app
118
+
119
+ # Copy package files
120
+ COPY package*.json ./
121
+
122
+ # Install dependencies
123
+ RUN npm ci
124
+
125
+ # Copy source
126
+ COPY . .
127
+
128
+ # Build the application
129
+ RUN npm run build
130
+
131
+ # Stage 2: Production with nginx
132
+ FROM nginx:${nginxVersion}-alpine AS production
133
+
134
+ ${labelSection}
135
+ # Remove default nginx config
136
+ RUN rm /etc/nginx/conf.d/default.conf
137
+
138
+ # Create non-root user if not nginx
139
+ USER ${user}
140
+
141
+ WORKDIR /usr/share/nginx/html
142
+
143
+ # Copy built files
144
+ COPY --from=builder --chown=${user}:${user} /app/${buildDir} .
145
+
146
+ # Copy custom nginx config
147
+ COPY --chown=${user}:${user} nginx.conf /etc/nginx/conf.d/app.conf
148
+
149
+ EXPOSE ${port}
150
+
151
+ # Health check
152
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\
153
+ CMD wget --no-verbose --tries=1 --spider http://localhost:${port}/ || exit 1
154
+
155
+ CMD ["nginx", "-g", "daemon off;"]
156
+ `;
157
+ }
158
+
159
+ /**
160
+ * Generate customizable base Dockerfile
161
+ */
162
+ export function generateBaseDockerfile(options = {}) {
163
+ const {
164
+ baseImage,
165
+ user = 'appuser',
166
+ workdir = '/app',
167
+ maintainer = '',
168
+ packages = [],
169
+ } = options;
170
+
171
+ if (!baseImage) {
172
+ throw new Error('baseImage is required');
173
+ }
174
+
175
+ const isAlpine = baseImage.includes('alpine');
176
+ const labelSection = maintainer ? `LABEL maintainer="${maintainer}"\n` : '';
177
+
178
+ let packageInstall = '';
179
+ if (packages.length > 0) {
180
+ if (isAlpine) {
181
+ packageInstall = `RUN apk add --no-cache ${packages.join(' ')}\n`;
182
+ } else {
183
+ packageInstall = `RUN apt-get update && apt-get install -y --no-install-recommends ${packages.join(' ')} && rm -rf /var/lib/apt/lists/*\n`;
184
+ }
185
+ } else {
186
+ // Add minimal security hardening
187
+ if (isAlpine) {
188
+ packageInstall = `RUN apk add --no-cache dumb-init\n`;
189
+ } else {
190
+ packageInstall = `RUN apt-get update && apt-get install -y --no-install-recommends dumb-init && rm -rf /var/lib/apt/lists/*\n`;
191
+ }
192
+ }
193
+
194
+ // Create non-root user
195
+ let userSetup = '';
196
+ if (user !== 'root') {
197
+ if (isAlpine) {
198
+ userSetup = `
199
+ # Create non-root user
200
+ RUN addgroup -g 1001 ${user} && adduser -u 1001 -G ${user} -s /bin/sh -D ${user}
201
+ `;
202
+ } else {
203
+ userSetup = `
204
+ # Create non-root user
205
+ RUN groupadd -g 1001 ${user} && useradd -u 1001 -g ${user} -s /bin/bash -m ${user}
206
+ `;
207
+ }
208
+ }
209
+
210
+ return `FROM ${baseImage}
211
+
212
+ ${labelSection}${packageInstall}${userSetup}
213
+ WORKDIR ${workdir}
214
+
215
+ USER ${user}
216
+ `;
217
+ }
218
+
219
+ /**
220
+ * Generate nginx config for security headers
221
+ */
222
+ export function generateNginxConfig(options = {}) {
223
+ const { port = 80, upstream = null } = options;
224
+
225
+ const upstreamBlock = upstream
226
+ ? `
227
+ upstream backend {
228
+ server ${upstream};
229
+ }
230
+ `
231
+ : '';
232
+
233
+ const proxyBlock = upstream
234
+ ? `
235
+ location /api {
236
+ proxy_pass http://backend;
237
+ proxy_http_version 1.1;
238
+ proxy_set_header Upgrade $http_upgrade;
239
+ proxy_set_header Connection 'upgrade';
240
+ proxy_set_header Host $host;
241
+ proxy_set_header X-Real-IP $remote_addr;
242
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
243
+ proxy_set_header X-Forwarded-Proto $scheme;
244
+ proxy_cache_bypass $http_upgrade;
245
+ }
246
+ `
247
+ : '';
248
+
249
+ return `${upstreamBlock}
250
+ server {
251
+ listen ${port};
252
+ server_name _;
253
+ root /usr/share/nginx/html;
254
+ index index.html;
255
+
256
+ # Security headers
257
+ add_header X-Content-Type-Options "nosniff" always;
258
+ add_header X-Frame-Options "SAMEORIGIN" always;
259
+ add_header X-XSS-Protection "1; mode=block" always;
260
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
261
+
262
+ # Gzip compression
263
+ gzip on;
264
+ gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
265
+
266
+ # SPA routing
267
+ location / {
268
+ try_files $uri $uri/ /index.html;
269
+ }
270
+ ${proxyBlock}
271
+ # Static file caching
272
+ location ~* \\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
273
+ expires 1y;
274
+ add_header Cache-Control "public, immutable";
275
+ }
276
+ }
277
+ `;
278
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Hardened Dockerfile Templates Tests
3
+ */
4
+ import { describe, it, expect } from 'vitest';
5
+ import {
6
+ generateServerDockerfile,
7
+ generateDashboardDockerfile,
8
+ generateBaseDockerfile,
9
+ SECURITY_HEADERS,
10
+ } from './dockerfile-templates.js';
11
+ import { checkDockerfileCompliance } from './cis-benchmark.js';
12
+ import { lintDockerfile } from './dockerfile-linter.js';
13
+
14
+ describe('dockerfile-templates', () => {
15
+ describe('generateServerDockerfile', () => {
16
+ it('generates a valid Dockerfile', () => {
17
+ const dockerfile = generateServerDockerfile();
18
+ expect(dockerfile).toContain('FROM');
19
+ expect(dockerfile).toContain('USER');
20
+ expect(dockerfile).toContain('HEALTHCHECK');
21
+ });
22
+
23
+ it('uses multi-stage build', () => {
24
+ const dockerfile = generateServerDockerfile();
25
+ const fromCount = (dockerfile.match(/^FROM /gm) || []).length;
26
+ expect(fromCount).toBeGreaterThanOrEqual(2);
27
+ });
28
+
29
+ it('runs as non-root user', () => {
30
+ const dockerfile = generateServerDockerfile();
31
+ expect(dockerfile).toMatch(/USER\s+(?!root)\w+/i);
32
+ });
33
+
34
+ it('has HEALTHCHECK instruction', () => {
35
+ const dockerfile = generateServerDockerfile();
36
+ expect(dockerfile).toContain('HEALTHCHECK');
37
+ });
38
+
39
+ it('uses specific version tags, not latest', () => {
40
+ const dockerfile = generateServerDockerfile();
41
+ expect(dockerfile).not.toMatch(/FROM\s+\w+:latest/i);
42
+ });
43
+
44
+ it('drops privileges with dumb-init or tini', () => {
45
+ const dockerfile = generateServerDockerfile();
46
+ expect(dockerfile).toMatch(/dumb-init|tini/i);
47
+ });
48
+
49
+ it('copies only necessary files', () => {
50
+ const dockerfile = generateServerDockerfile();
51
+ expect(dockerfile).toContain('COPY --from=');
52
+ });
53
+
54
+ it('sets NODE_ENV to production', () => {
55
+ const dockerfile = generateServerDockerfile();
56
+ expect(dockerfile).toMatch(/ENV\s+NODE_ENV\s*=?\s*production/i);
57
+ });
58
+
59
+ it('exposes correct port', () => {
60
+ const dockerfile = generateServerDockerfile({ port: 5001 });
61
+ expect(dockerfile).toContain('EXPOSE 5001');
62
+ });
63
+
64
+ it('passes CIS benchmark checks', () => {
65
+ const dockerfile = generateServerDockerfile();
66
+ const result = checkDockerfileCompliance(dockerfile);
67
+ // Should have no high severity findings
68
+ const highFindings = result.findings.filter(f => f.severity === 'high');
69
+ expect(highFindings.length).toBe(0);
70
+ });
71
+
72
+ it('passes dockerfile linter', () => {
73
+ const dockerfile = generateServerDockerfile();
74
+ const result = lintDockerfile(dockerfile);
75
+ const critical = result.findings.filter(f => f.severity === 'critical');
76
+ expect(critical.length).toBe(0);
77
+ });
78
+ });
79
+
80
+ describe('generateDashboardDockerfile', () => {
81
+ it('generates a valid Dockerfile for React/Vite', () => {
82
+ const dockerfile = generateDashboardDockerfile();
83
+ expect(dockerfile).toContain('FROM');
84
+ expect(dockerfile).toContain('npm run build');
85
+ });
86
+
87
+ it('uses nginx for serving static files', () => {
88
+ const dockerfile = generateDashboardDockerfile();
89
+ expect(dockerfile).toContain('nginx');
90
+ });
91
+
92
+ it('uses multi-stage build', () => {
93
+ const dockerfile = generateDashboardDockerfile();
94
+ const fromCount = (dockerfile.match(/^FROM /gm) || []).length;
95
+ expect(fromCount).toBeGreaterThanOrEqual(2);
96
+ });
97
+
98
+ it('runs nginx as non-root', () => {
99
+ const dockerfile = generateDashboardDockerfile();
100
+ expect(dockerfile).toMatch(/USER\s+(?!root)\w+/i);
101
+ });
102
+
103
+ it('has HEALTHCHECK', () => {
104
+ const dockerfile = generateDashboardDockerfile();
105
+ expect(dockerfile).toContain('HEALTHCHECK');
106
+ });
107
+
108
+ it('removes default nginx config', () => {
109
+ const dockerfile = generateDashboardDockerfile();
110
+ expect(dockerfile).toMatch(/rm.*default\.conf|COPY.*nginx\.conf/i);
111
+ });
112
+
113
+ it('exposes correct port', () => {
114
+ const dockerfile = generateDashboardDockerfile({ port: 80 });
115
+ expect(dockerfile).toContain('EXPOSE 80');
116
+ });
117
+
118
+ it('passes CIS benchmark checks', () => {
119
+ const dockerfile = generateDashboardDockerfile();
120
+ const result = checkDockerfileCompliance(dockerfile);
121
+ const highFindings = result.findings.filter(f => f.severity === 'high');
122
+ expect(highFindings.length).toBe(0);
123
+ });
124
+ });
125
+
126
+ describe('generateBaseDockerfile', () => {
127
+ it('generates customizable base image', () => {
128
+ const dockerfile = generateBaseDockerfile({
129
+ baseImage: 'node:20-alpine',
130
+ user: 'appuser',
131
+ workdir: '/app',
132
+ });
133
+ expect(dockerfile).toContain('FROM node:20-alpine');
134
+ expect(dockerfile).toContain('USER appuser');
135
+ expect(dockerfile).toContain('WORKDIR /app');
136
+ });
137
+
138
+ it('includes security hardening by default', () => {
139
+ const dockerfile = generateBaseDockerfile({ baseImage: 'node:20-alpine' });
140
+ // Should have no shell or setuid binaries
141
+ expect(dockerfile).toMatch(/apk.*--no-cache|apt-get.*--no-install-recommends/i);
142
+ });
143
+
144
+ it('adds LABEL for maintainer', () => {
145
+ const dockerfile = generateBaseDockerfile({
146
+ baseImage: 'node:20-alpine',
147
+ maintainer: 'team@example.com',
148
+ });
149
+ expect(dockerfile).toContain('LABEL maintainer=');
150
+ });
151
+ });
152
+
153
+ describe('SECURITY_HEADERS', () => {
154
+ it('includes nginx security headers', () => {
155
+ expect(SECURITY_HEADERS.nginx).toBeDefined();
156
+ expect(SECURITY_HEADERS.nginx).toContain('X-Content-Type-Options');
157
+ expect(SECURITY_HEADERS.nginx).toContain('X-Frame-Options');
158
+ });
159
+
160
+ it('includes express security headers', () => {
161
+ expect(SECURITY_HEADERS.express).toBeDefined();
162
+ });
163
+ });
164
+ });