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,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
|
+
});
|