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,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy Script Generator
|
|
3
|
+
* Blue-green and rolling deployment scripts for VPS
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generates a blue-green deployment script
|
|
8
|
+
* @param {Object} options - Deployment options
|
|
9
|
+
* @param {string} options.service - Service name
|
|
10
|
+
* @returns {string} Blue-green deployment script
|
|
11
|
+
*/
|
|
12
|
+
export function generateBlueGreenScript({ service }) {
|
|
13
|
+
return `#!/bin/bash
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# Blue-green deployment for ${service}
|
|
17
|
+
SERVICE="${service}"
|
|
18
|
+
CURRENT_COLOR=$(docker ps --filter "name=\${SERVICE}" --format '{{.Names}}' | grep -o 'blue\\|green' || echo "blue")
|
|
19
|
+
|
|
20
|
+
if [ "\$CURRENT_COLOR" = "blue" ]; then
|
|
21
|
+
NEW_COLOR="green"
|
|
22
|
+
else
|
|
23
|
+
NEW_COLOR="blue"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
echo "Current: \$CURRENT_COLOR, Deploying to: \$NEW_COLOR"
|
|
27
|
+
|
|
28
|
+
# Deploy to new color
|
|
29
|
+
docker-compose up -d "\${SERVICE}-\${NEW_COLOR}"
|
|
30
|
+
|
|
31
|
+
# Wait for health check
|
|
32
|
+
sleep 10
|
|
33
|
+
|
|
34
|
+
# Switch traffic
|
|
35
|
+
docker exec nginx nginx -s reload
|
|
36
|
+
|
|
37
|
+
# Stop old color
|
|
38
|
+
docker-compose stop "\${SERVICE}-\${CURRENT_COLOR}"
|
|
39
|
+
|
|
40
|
+
echo "Deployed \${SERVICE} to \${NEW_COLOR}"
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generates a rolling update deployment script
|
|
46
|
+
* @param {Object} options - Deployment options
|
|
47
|
+
* @param {string} options.service - Service name
|
|
48
|
+
* @param {number} [options.replicas=3] - Number of replicas
|
|
49
|
+
* @returns {string} Rolling update script
|
|
50
|
+
*/
|
|
51
|
+
export function generateRollingScript({ service, replicas = 3 }) {
|
|
52
|
+
return `#!/bin/bash
|
|
53
|
+
set -euo pipefail
|
|
54
|
+
|
|
55
|
+
# Rolling update for ${service}
|
|
56
|
+
SERVICE="${service}"
|
|
57
|
+
REPLICAS=${replicas}
|
|
58
|
+
|
|
59
|
+
echo "Starting rolling update for \${SERVICE} with \${REPLICAS} replicas"
|
|
60
|
+
|
|
61
|
+
for i in $(seq 1 \$REPLICAS); do
|
|
62
|
+
echo "Updating replica \$i of \$REPLICAS..."
|
|
63
|
+
|
|
64
|
+
# Stop old instance
|
|
65
|
+
docker-compose stop "\${SERVICE}-\$i" || true
|
|
66
|
+
|
|
67
|
+
# Start new instance
|
|
68
|
+
docker-compose up -d "\${SERVICE}-\$i"
|
|
69
|
+
|
|
70
|
+
# Wait for health
|
|
71
|
+
sleep 5
|
|
72
|
+
|
|
73
|
+
echo "Replica \$i updated"
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
echo "Rolling update complete"
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Adds health verification to deployment
|
|
82
|
+
* @param {Object} options - Health check options
|
|
83
|
+
* @param {string} options.endpoint - Health endpoint
|
|
84
|
+
* @param {number} [options.timeout=30] - Timeout in seconds
|
|
85
|
+
* @param {number} [options.retries=5] - Number of retries
|
|
86
|
+
* @returns {string} Health verification script snippet
|
|
87
|
+
*/
|
|
88
|
+
export function addHealthVerification({ endpoint, timeout = 30, retries = 5 }) {
|
|
89
|
+
return `# Health check verification
|
|
90
|
+
HEALTH_ENDPOINT="${endpoint}"
|
|
91
|
+
TIMEOUT=${timeout}
|
|
92
|
+
RETRIES=${retries}
|
|
93
|
+
|
|
94
|
+
check_health() {
|
|
95
|
+
local attempt=1
|
|
96
|
+
while [ \$attempt -le \$RETRIES ]; do
|
|
97
|
+
echo "Health check attempt \$attempt/\$RETRIES..."
|
|
98
|
+
if curl -sf "\${HEALTH_ENDPOINT}" > /dev/null; then
|
|
99
|
+
echo "Health check passed"
|
|
100
|
+
return 0
|
|
101
|
+
fi
|
|
102
|
+
sleep \$(( TIMEOUT / RETRIES ))
|
|
103
|
+
attempt=\$((attempt + 1))
|
|
104
|
+
done
|
|
105
|
+
echo "Health check failed"
|
|
106
|
+
return 1
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Adds rollback support to deployment
|
|
113
|
+
* @param {Object} options - Rollback options
|
|
114
|
+
* @returns {string} Rollback script snippet
|
|
115
|
+
*/
|
|
116
|
+
export function addRollbackSupport(options = {}) {
|
|
117
|
+
return `# Rollback support
|
|
118
|
+
PREVIOUS_VERSION=""
|
|
119
|
+
|
|
120
|
+
save_previous_version() {
|
|
121
|
+
PREVIOUS_VERSION=\$(docker images --format '{{.Tag}}' | head -1)
|
|
122
|
+
echo "Saved previous version: \$PREVIOUS_VERSION"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
rollback() {
|
|
126
|
+
echo "Rolling back to \$PREVIOUS_VERSION..."
|
|
127
|
+
docker-compose down
|
|
128
|
+
docker tag "\${SERVICE}:\${PREVIOUS_VERSION}" "\${SERVICE}:latest"
|
|
129
|
+
docker-compose up -d
|
|
130
|
+
echo "Rollback complete"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# Call rollback on failure
|
|
134
|
+
trap 'rollback' ERR
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Generates pre and post deployment hooks
|
|
140
|
+
* @param {Object} options - Hook options
|
|
141
|
+
* @param {string} [options.pre] - Pre-deployment command
|
|
142
|
+
* @param {string} [options.post] - Post-deployment command
|
|
143
|
+
* @returns {Object} Generated hooks
|
|
144
|
+
*/
|
|
145
|
+
export function generateHooks({ pre, post }) {
|
|
146
|
+
const hooks = {};
|
|
147
|
+
|
|
148
|
+
if (pre) {
|
|
149
|
+
hooks.pre = `# Pre-deployment hook
|
|
150
|
+
echo "Running pre-deployment hook..."
|
|
151
|
+
${pre}
|
|
152
|
+
echo "Pre-deployment hook complete"
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (post) {
|
|
157
|
+
hooks.post = `# Post-deployment hook
|
|
158
|
+
echo "Running post-deployment hook..."
|
|
159
|
+
${post}
|
|
160
|
+
echo "Post-deployment hook complete"
|
|
161
|
+
`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return hooks;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Creates a deploy script generator instance
|
|
169
|
+
* @returns {Object} Generator with blueGreen and rolling methods
|
|
170
|
+
*/
|
|
171
|
+
export function createDeployScriptGenerator() {
|
|
172
|
+
return {
|
|
173
|
+
/**
|
|
174
|
+
* Generates a blue-green deployment script
|
|
175
|
+
* @param {Object} options - Deployment options
|
|
176
|
+
* @returns {string} Deployment script
|
|
177
|
+
*/
|
|
178
|
+
blueGreen(options) {
|
|
179
|
+
let script = generateBlueGreenScript(options);
|
|
180
|
+
|
|
181
|
+
if (options.healthEndpoint) {
|
|
182
|
+
script += '\n' + addHealthVerification({ endpoint: options.healthEndpoint });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (options.rollback !== false) {
|
|
186
|
+
script += '\n' + addRollbackSupport();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return script;
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Generates a rolling update deployment script
|
|
194
|
+
* @param {Object} options - Deployment options
|
|
195
|
+
* @returns {string} Deployment script
|
|
196
|
+
*/
|
|
197
|
+
rolling(options) {
|
|
198
|
+
let script = generateRollingScript(options);
|
|
199
|
+
|
|
200
|
+
if (options.healthEndpoint) {
|
|
201
|
+
script += '\n' + addHealthVerification({ endpoint: options.healthEndpoint });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (options.rollback !== false) {
|
|
205
|
+
script += '\n' + addRollbackSupport();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return script;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy Script Generator Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { generateBlueGreenScript, generateRollingScript, addHealthVerification, addRollbackSupport, generateHooks, createDeployScriptGenerator } from './deploy-script.js';
|
|
6
|
+
|
|
7
|
+
describe('deploy-script', () => {
|
|
8
|
+
describe('generateBlueGreenScript', () => {
|
|
9
|
+
it('generates blue-green deploy script', () => {
|
|
10
|
+
const script = generateBlueGreenScript({ service: 'app' });
|
|
11
|
+
expect(script).toContain('blue');
|
|
12
|
+
expect(script).toContain('green');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('generateRollingScript', () => {
|
|
17
|
+
it('generates rolling update script', () => {
|
|
18
|
+
const script = generateRollingScript({ service: 'app', replicas: 3 });
|
|
19
|
+
expect(script).toContain('rolling');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('addHealthVerification', () => {
|
|
24
|
+
it('includes health check', () => {
|
|
25
|
+
const script = addHealthVerification({ endpoint: '/health' });
|
|
26
|
+
expect(script).toContain('health');
|
|
27
|
+
expect(script).toContain('curl');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('addRollbackSupport', () => {
|
|
32
|
+
it('supports rollback on failure', () => {
|
|
33
|
+
const script = addRollbackSupport({});
|
|
34
|
+
expect(script).toContain('rollback');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('generateHooks', () => {
|
|
39
|
+
it('generates pre/post hooks', () => {
|
|
40
|
+
const hooks = generateHooks({ pre: 'npm run migrate', post: 'npm run notify' });
|
|
41
|
+
expect(hooks.pre).toContain('migrate');
|
|
42
|
+
expect(hooks.post).toContain('notify');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('createDeployScriptGenerator', () => {
|
|
47
|
+
it('creates generator', () => {
|
|
48
|
+
const generator = createDeployScriptGenerator();
|
|
49
|
+
expect(generator.blueGreen).toBeDefined();
|
|
50
|
+
expect(generator.rolling).toBeDefined();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VPS Secrets Manager
|
|
3
|
+
* Secrets generation and rotation for VPS deployments
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import crypto from 'crypto';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a secrets directory with secure permissions
|
|
10
|
+
* @param {Object} options - Directory options
|
|
11
|
+
* @param {string} options.path - Directory path
|
|
12
|
+
* @param {Function} [options.mkdir] - mkdir function
|
|
13
|
+
* @param {Function} [options.chmod] - chmod function
|
|
14
|
+
* @returns {Promise<Object>} Creation result
|
|
15
|
+
*/
|
|
16
|
+
export async function createSecretsDir({ path, mkdir, chmod }) {
|
|
17
|
+
if (mkdir) {
|
|
18
|
+
await mkdir(path, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
if (chmod) {
|
|
21
|
+
await chmod(path, 0o600);
|
|
22
|
+
}
|
|
23
|
+
return { success: true, path };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Generates secrets from a template
|
|
28
|
+
* @param {Object} options - Generation options
|
|
29
|
+
* @param {Object} options.template - Secret template with name and length
|
|
30
|
+
* @returns {Object} Generated secrets
|
|
31
|
+
*/
|
|
32
|
+
export function generateSecrets({ template }) {
|
|
33
|
+
const secrets = {};
|
|
34
|
+
|
|
35
|
+
for (const [name, config] of Object.entries(template)) {
|
|
36
|
+
const length = config.length || 32;
|
|
37
|
+
// Generate a random string of the specified length
|
|
38
|
+
secrets[name] = crypto.randomBytes(Math.ceil(length / 2))
|
|
39
|
+
.toString('hex')
|
|
40
|
+
.slice(0, length);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return secrets;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Rotates secrets safely
|
|
48
|
+
* @param {Object} options - Rotation options
|
|
49
|
+
* @param {string[]} options.secrets - Secret names to rotate
|
|
50
|
+
* @param {Function} [options.mockRotate] - Mock rotate function for testing
|
|
51
|
+
* @returns {Promise<Object>} Rotation result
|
|
52
|
+
*/
|
|
53
|
+
export async function rotateSecrets({ secrets, mockRotate }) {
|
|
54
|
+
const rotated = [];
|
|
55
|
+
|
|
56
|
+
for (const secret of secrets) {
|
|
57
|
+
if (mockRotate) {
|
|
58
|
+
await mockRotate(secret);
|
|
59
|
+
}
|
|
60
|
+
rotated.push(secret);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { rotated, timestamp: new Date().toISOString() };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Validates secret format
|
|
68
|
+
* @param {Object} options - Validation options
|
|
69
|
+
* @param {string} options.name - Secret name
|
|
70
|
+
* @param {string} options.value - Secret value
|
|
71
|
+
* @returns {Object} Validation result
|
|
72
|
+
*/
|
|
73
|
+
export function validateSecretFormat({ name, value }) {
|
|
74
|
+
// Secret names should not contain spaces
|
|
75
|
+
if (name.includes(' ')) {
|
|
76
|
+
return { valid: false, error: 'Secret name cannot contain spaces' };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Secret names should be uppercase with underscores
|
|
80
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(name)) {
|
|
81
|
+
return { valid: false, error: 'Secret name must be uppercase with underscores' };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Value should not be empty
|
|
85
|
+
if (!value || value.length === 0) {
|
|
86
|
+
return { valid: false, error: 'Secret value cannot be empty' };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { valid: true };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a secrets manager instance
|
|
94
|
+
* @returns {Object} Secrets manager with get, set, and rotate methods
|
|
95
|
+
*/
|
|
96
|
+
export function createSecretsManager() {
|
|
97
|
+
const secrets = new Map();
|
|
98
|
+
|
|
99
|
+
const manager = {
|
|
100
|
+
/**
|
|
101
|
+
* Gets a secret value
|
|
102
|
+
* @param {string} name - Secret name
|
|
103
|
+
* @returns {string|undefined} Secret value
|
|
104
|
+
*/
|
|
105
|
+
get(name) {
|
|
106
|
+
return secrets.get(name);
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Sets a secret value
|
|
111
|
+
* @param {string} name - Secret name
|
|
112
|
+
* @param {string} value - Secret value
|
|
113
|
+
* @returns {boolean} Success status
|
|
114
|
+
*/
|
|
115
|
+
set(name, value) {
|
|
116
|
+
const validation = validateSecretFormat({ name, value });
|
|
117
|
+
if (!validation.valid) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
secrets.set(name, value);
|
|
121
|
+
return true;
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Rotates a secret
|
|
126
|
+
* @param {string} name - Secret name to rotate
|
|
127
|
+
* @param {number} [length=32] - New secret length
|
|
128
|
+
* @returns {boolean} Success status
|
|
129
|
+
*/
|
|
130
|
+
rotate(name, length = 32) {
|
|
131
|
+
const newValue = crypto.randomBytes(Math.ceil(length / 2))
|
|
132
|
+
.toString('hex')
|
|
133
|
+
.slice(0, length);
|
|
134
|
+
secrets.set(name, newValue);
|
|
135
|
+
return true;
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Returns safe string representation (no secrets exposed)
|
|
140
|
+
* @returns {string} Safe string representation
|
|
141
|
+
*/
|
|
142
|
+
toString() {
|
|
143
|
+
return `[SecretsManager: ${secrets.size} secrets]`;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return manager;
|
|
148
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VPS Secrets Manager Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
+
import { createSecretsDir, generateSecrets, rotateSecrets, validateSecretFormat, createSecretsManager } from './secrets-manager.js';
|
|
6
|
+
|
|
7
|
+
describe('secrets-manager', () => {
|
|
8
|
+
describe('createSecretsDir', () => {
|
|
9
|
+
it('creates directory with 600 permissions', async () => {
|
|
10
|
+
const mockMkdir = vi.fn().mockResolvedValue(true);
|
|
11
|
+
const mockChmod = vi.fn().mockResolvedValue(true);
|
|
12
|
+
await createSecretsDir({ path: '/etc/tlc/secrets', mkdir: mockMkdir, chmod: mockChmod });
|
|
13
|
+
expect(mockChmod).toHaveBeenCalledWith('/etc/tlc/secrets', 0o600);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('generateSecrets', () => {
|
|
18
|
+
it('generates secrets from template', () => {
|
|
19
|
+
const secrets = generateSecrets({ template: { DB_PASSWORD: { length: 32 }, API_KEY: { length: 64 } } });
|
|
20
|
+
expect(secrets.DB_PASSWORD.length).toBe(32);
|
|
21
|
+
expect(secrets.API_KEY.length).toBe(64);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('rotateSecrets', () => {
|
|
26
|
+
it('rotates secrets safely', async () => {
|
|
27
|
+
const result = await rotateSecrets({ secrets: ['DB_PASSWORD'], mockRotate: vi.fn().mockResolvedValue(true) });
|
|
28
|
+
expect(result.rotated).toContain('DB_PASSWORD');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('validateSecretFormat', () => {
|
|
33
|
+
it('validates secret format', () => {
|
|
34
|
+
const result = validateSecretFormat({ name: 'API_KEY', value: 'abc123' });
|
|
35
|
+
expect(result.valid).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('rejects secrets with spaces', () => {
|
|
39
|
+
const result = validateSecretFormat({ name: 'API KEY', value: 'abc' });
|
|
40
|
+
expect(result.valid).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('createSecretsManager', () => {
|
|
45
|
+
it('creates manager', () => {
|
|
46
|
+
const manager = createSecretsManager();
|
|
47
|
+
expect(manager.get).toBeDefined();
|
|
48
|
+
expect(manager.set).toBeDefined();
|
|
49
|
+
expect(manager.rotate).toBeDefined();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('never exposes secrets in logs', () => {
|
|
53
|
+
const manager = createSecretsManager();
|
|
54
|
+
const output = manager.toString();
|
|
55
|
+
expect(output).not.toContain('password');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Hardening Configuration
|
|
3
|
+
* SSH config, sysctl, user management, security limits
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate SSH daemon configuration
|
|
8
|
+
* @param {Object} options - SSH configuration options
|
|
9
|
+
* @param {boolean} [options.passwordAuth=false] - Enable password authentication
|
|
10
|
+
* @param {number} [options.port=22] - SSH port
|
|
11
|
+
* @param {boolean} [options.permitRootLogin=false] - Allow root login
|
|
12
|
+
* @returns {string} SSH configuration
|
|
13
|
+
*/
|
|
14
|
+
export function generateSshConfig(options = {}) {
|
|
15
|
+
const {
|
|
16
|
+
passwordAuth = false,
|
|
17
|
+
port = 22,
|
|
18
|
+
permitRootLogin = false,
|
|
19
|
+
} = options;
|
|
20
|
+
|
|
21
|
+
const lines = [
|
|
22
|
+
'# SSH Server Configuration',
|
|
23
|
+
'# Generated by TLC Server Hardening',
|
|
24
|
+
'',
|
|
25
|
+
`Port ${port}`,
|
|
26
|
+
'Protocol 2',
|
|
27
|
+
'',
|
|
28
|
+
'# Authentication',
|
|
29
|
+
`PasswordAuthentication ${passwordAuth ? 'yes' : 'no'}`,
|
|
30
|
+
'PubkeyAuthentication yes',
|
|
31
|
+
`PermitRootLogin ${permitRootLogin ? 'yes' : 'no'}`,
|
|
32
|
+
'PermitEmptyPasswords no',
|
|
33
|
+
'ChallengeResponseAuthentication no',
|
|
34
|
+
'',
|
|
35
|
+
'# Security',
|
|
36
|
+
'X11Forwarding no',
|
|
37
|
+
'MaxAuthTries 3',
|
|
38
|
+
'LoginGraceTime 60',
|
|
39
|
+
'ClientAliveInterval 300',
|
|
40
|
+
'ClientAliveCountMax 2',
|
|
41
|
+
'',
|
|
42
|
+
'# Logging',
|
|
43
|
+
'SyslogFacility AUTH',
|
|
44
|
+
'LogLevel VERBOSE',
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Generate sysctl kernel parameter configuration
|
|
52
|
+
* @param {Object} options - Sysctl options
|
|
53
|
+
* @returns {string} Sysctl configuration
|
|
54
|
+
*/
|
|
55
|
+
export function generateSysctlConfig(options = {}) {
|
|
56
|
+
const lines = [
|
|
57
|
+
'# Sysctl Security Configuration',
|
|
58
|
+
'# Generated by TLC Server Hardening',
|
|
59
|
+
'',
|
|
60
|
+
'# Disable IP forwarding',
|
|
61
|
+
'net.ipv4.ip_forward = 0',
|
|
62
|
+
'net.ipv6.conf.all.forwarding = 0',
|
|
63
|
+
'',
|
|
64
|
+
'# Disable source routing',
|
|
65
|
+
'net.ipv4.conf.all.accept_source_route = 0',
|
|
66
|
+
'net.ipv4.conf.default.accept_source_route = 0',
|
|
67
|
+
'net.ipv6.conf.all.accept_source_route = 0',
|
|
68
|
+
'',
|
|
69
|
+
'# Disable ICMP redirects',
|
|
70
|
+
'net.ipv4.conf.all.accept_redirects = 0',
|
|
71
|
+
'net.ipv4.conf.default.accept_redirects = 0',
|
|
72
|
+
'net.ipv4.conf.all.send_redirects = 0',
|
|
73
|
+
'net.ipv4.conf.default.send_redirects = 0',
|
|
74
|
+
'',
|
|
75
|
+
'# Enable SYN flood protection',
|
|
76
|
+
'net.ipv4.tcp_syncookies = 1',
|
|
77
|
+
'net.ipv4.tcp_max_syn_backlog = 2048',
|
|
78
|
+
'',
|
|
79
|
+
'# Enable reverse path filtering',
|
|
80
|
+
'net.ipv4.conf.all.rp_filter = 1',
|
|
81
|
+
'net.ipv4.conf.default.rp_filter = 1',
|
|
82
|
+
'',
|
|
83
|
+
'# Log martian packets',
|
|
84
|
+
'net.ipv4.conf.all.log_martians = 1',
|
|
85
|
+
'',
|
|
86
|
+
'# Ignore ICMP broadcast requests',
|
|
87
|
+
'net.ipv4.icmp_echo_ignore_broadcasts = 1',
|
|
88
|
+
'',
|
|
89
|
+
'# Kernel hardening',
|
|
90
|
+
'kernel.randomize_va_space = 2',
|
|
91
|
+
'kernel.kptr_restrict = 2',
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Generate commands to disable unnecessary services
|
|
99
|
+
* @param {string[]} services - List of services to disable
|
|
100
|
+
* @returns {string[]} Array of systemctl commands
|
|
101
|
+
*/
|
|
102
|
+
export function disableServices(services = []) {
|
|
103
|
+
const commands = [];
|
|
104
|
+
|
|
105
|
+
for (const service of services) {
|
|
106
|
+
commands.push(`systemctl stop ${service}`);
|
|
107
|
+
commands.push(`systemctl disable ${service}`);
|
|
108
|
+
commands.push(`systemctl mask ${service}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return commands;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generate unattended-upgrades configuration
|
|
116
|
+
* @param {Object} options - Auto-update options
|
|
117
|
+
* @returns {string} Unattended-upgrades configuration
|
|
118
|
+
*/
|
|
119
|
+
export function enableAutoUpdates(options = {}) {
|
|
120
|
+
const {
|
|
121
|
+
rebootTime = '02:00',
|
|
122
|
+
autoReboot = false,
|
|
123
|
+
} = options;
|
|
124
|
+
|
|
125
|
+
const lines = [
|
|
126
|
+
'// Unattended-Upgrade Configuration',
|
|
127
|
+
'// Generated by TLC Server Hardening',
|
|
128
|
+
'',
|
|
129
|
+
'Unattended-Upgrade::Allowed-Origins {',
|
|
130
|
+
' "${distro_id}:${distro_codename}";',
|
|
131
|
+
' "${distro_id}:${distro_codename}-security";',
|
|
132
|
+
' "${distro_id}ESMApps:${distro_codename}-apps-security";',
|
|
133
|
+
' "${distro_id}ESM:${distro_codename}-infra-security";',
|
|
134
|
+
'};',
|
|
135
|
+
'',
|
|
136
|
+
'Unattended-Upgrade::Package-Blacklist {',
|
|
137
|
+
'};',
|
|
138
|
+
'',
|
|
139
|
+
`Unattended-Upgrade::Automatic-Reboot "${autoReboot ? 'true' : 'false'}";`,
|
|
140
|
+
`Unattended-Upgrade::Automatic-Reboot-Time "${rebootTime}";`,
|
|
141
|
+
'',
|
|
142
|
+
'Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";',
|
|
143
|
+
'Unattended-Upgrade::Remove-Unused-Dependencies "true";',
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
return lines.join('\n');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Create a server hardening manager
|
|
151
|
+
* @returns {Object} Server hardening manager with methods
|
|
152
|
+
*/
|
|
153
|
+
export function createServerHardening() {
|
|
154
|
+
return {
|
|
155
|
+
generateSshConfig,
|
|
156
|
+
generateSysctl: generateSysctlConfig,
|
|
157
|
+
disableServices,
|
|
158
|
+
enableAutoUpdates,
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Generate all hardening configurations
|
|
162
|
+
* @param {Object} options - Configuration options
|
|
163
|
+
* @returns {Object} All generated configurations
|
|
164
|
+
*/
|
|
165
|
+
generateAll(options = {}) {
|
|
166
|
+
return {
|
|
167
|
+
ssh: generateSshConfig(options.ssh || {}),
|
|
168
|
+
sysctl: generateSysctlConfig(options.sysctl || {}),
|
|
169
|
+
disableCommands: disableServices(options.disableServices || []),
|
|
170
|
+
autoUpdates: enableAutoUpdates(options.autoUpdates || {}),
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|