import test from 'node:test'; import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; const servicePath = new URL('../deploy/timmy-staging.service', import.meta.url); const envPath = new URL('../deploy/timmy-staging.env.example', import.meta.url); const caddyPath = new URL('../deploy/Caddyfile.staging.example', import.meta.url); const runbookPath = new URL('../docs/STAGING-RUNBOOK.md', import.meta.url); const literalSecret = /(password|token|secret|api[_-]?key)\s*[=:]\s*(?!\$\{|<|CHANGE_ME|false|$)["']?[A-Za-z0-9/+_.-]{8,}/i; test('systemd template runs a dedicated loopback-only agent-disabled service', async () => { const service = await readFile(servicePath, 'utf8'); for (const directive of [ 'User=timmy-staging', 'Group=timmy-staging', 'WorkingDirectory=/opt/timmy-staging/current', 'EnvironmentFile=/etc/timmy-staging.env', 'Environment=HOST=127.0.0.1', 'Environment=PORT=4174', 'Environment=TIMMY_BASE_PATH=/timmy-staging', 'Environment=TIMMY_AGENT_ENABLED=false', ]) assert.match(service, new RegExp(`^${directive.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm'), directive); assert.match(service, /^ExecStart=\/usr\/bin\/node server\.mjs$/m); assert.doesNotMatch(service, /0\.0\.0\.0|TIMMY_AGENT_ENABLED=true/); assert.doesNotMatch(service, literalSecret); }); test('systemd template applies filesystem privilege process and resource confinement', async () => { const service = await readFile(servicePath, 'utf8'); for (const directive of [ 'UMask=0077', 'NoNewPrivileges=true', 'PrivateTmp=true', 'PrivateDevices=true', 'ProtectSystem=strict', 'ProtectHome=true', 'ReadWritePaths=/var/lib/timmy-staging', 'RestrictSUIDSGID=true', 'LockPersonality=true', 'RestrictNamespaces=true', 'ProtectKernelTunables=true', 'ProtectKernelModules=true', 'ProtectKernelLogs=true', 'ProtectControlGroups=true', 'ProtectClock=true', 'ProtectHostname=true', 'CapabilityBoundingSet=', 'AmbientCapabilities=', 'RestrictRealtime=true', 'TasksMax=64', 'MemoryMax=512M', 'LimitNOFILE=1024', ]) assert.match(service, new RegExp(`^${directive.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm'), directive); assert.equal((service.match(/^ReadWritePaths=/gm) || []).length, 1); }); test('environment example is least privilege base-path staging configuration without credentials', async () => { const env = await readFile(envPath, 'utf8'); for (const setting of [ 'HOST=127.0.0.1', 'PORT=4174', 'TIMMY_BASE_PATH=/timmy-staging', 'TIMMY_STAGING_LABEL=true', 'TIMMY_AGENT_ENABLED=false', 'TIMMY_VISION_ENABLED=false', ]) assert.match(env, new RegExp(`^${setting.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm')); assert.match(env, /install -m 600/); assert.doesNotMatch(env, /TIMMY_AGENT_ACCESS_TOKEN|GITEA_TOKEN|OPENAI_API_KEY|COOKIE/); assert.doesNotMatch(env, literalSecret); }); test('Caddy snippet isolates git and authenticates staging before a catchall', async () => { const caddy = await readFile(caddyPath, 'utf8'); const git = caddy.indexOf('handle @git'); const staging = caddy.indexOf('handle_path /timmy-staging/*'); const catchall = caddy.indexOf('handle {'); assert.ok(git >= 0 && staging > git && catchall > staging, 'route order must be git, staging, catchall'); assert.match(caddy, /basic_auth/); assert.match(caddy, /\{\$TIMMY_STAGING_PASSWORD_HASH\}/); assert.match(caddy, /reverse_proxy 127\.0\.0\.1:4174/); assert.match(caddy, /max_size 8MB/); assert.match(caddy, /X-Content-Type-Options "nosniff"/); assert.match(caddy, /X-Frame-Options "DENY"/); assert.match(caddy, /Referrer-Policy "no-referrer"/); assert.match(caddy, /Content-Security-Policy/); assert.match(caddy, /rewrite \* \/timmy-staging\{uri\}/, 'handle_path strips once, then upstream base path is explicitly reconstructed'); assert.doesNotMatch(caddy, /\$2[aby]\$[A-Za-z0-9./]{20,}|password\s+[^<{\s]/i); }); test('runbook covers safe installation operation verification rollback backup and removal', async () => { const runbook = await readFile(runbookPath, 'utf8'); for (const heading of [ 'Prerequisites', 'DNS and URL', 'Install', 'Promote', 'Smoke test', 'Status and logs', 'Rollback', 'Backup', 'Remove staging', 'Template validation', ]) assert.match(runbook, new RegExp(`^## .*${heading}`, 'mi'), heading); assert.match(runbook, /sha256/i); assert.match(runbook, /chmod 600|install -m 600/); assert.match(runbook, /systemctl restart timmy-staging\.service/); assert.match(runbook, /journalctl -u timmy-staging\.service/); assert.match(runbook, /do not.*live|approval/i); assert.doesNotMatch(runbook, literalSecret); });