'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('fs'); const http = require('http'); const os = require('os'); const path = require('path'); const { SettingsStore } = require('../../src/persistence/settings-store'); const { SecretsStore } = require('../../src/security/secrets-store'); const { CommandRegistry } = require('../../src/core/command-registry'); const { commandErrorCode, createAppServer } = require('../../src/server/app-server'); test('command failures expose stable machine-readable availability codes', () => { assert.equal(commandErrorCode(new Error('PS5-profiel ontbreekt.')), 'NOT_CONFIGURED'); assert.equal(commandErrorCode(new Error('Configureer eerst het IP-adres van je PS5.')), 'NOT_CONFIGURED'); assert.equal(commandErrorCode(Object.assign(new Error('connect failed'), { code: 'ECONNREFUSED' })), 'UNAVAILABLE'); assert.equal(commandErrorCode(new Error('Upstream HTTP 429')), 'UPSTREAM_UNAVAILABLE'); assert.equal(commandErrorCode(new Error('Ongeldige invoer.')), 'COMMAND_FAILED'); }); test('first-run, login session and CSRF protection work end to end', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'porkfolio-app-')); const settings = new SettingsStore(path.join(root, 'config', 'settings.json')); const secrets = new SecretsStore({ filePath: path.join(root, 'secrets', 'secrets.json'), keyFile: path.join(root, 'secrets', 'key') }); const registry = new CommandRegistry({ platform: 'server' }); let operationsStarted = 0; registry.maintenance = { startOperations: () => { operationsStarted += 1; } }; registry.register({ name: 'test:read', classification: 'read-only', handler: () => ({ value: 42 }) }); registry.register({ name: 'test:write', classification: 'mutating', handler: input => input }); registry.register({ name: 'test:large', classification: 'mutating', maxBodyBytes: 512 * 1024, handler: input => ({ length: input.payload.length }) }); const logger = { info() {}, warn() {}, error() {}, audit() {} }; process.env.PORKFOLIO_ALLOWED_HOSTS = '127.0.0.1'; const app = createAppServer({ appRoot: path.resolve(__dirname, '../..'), paths: { root, cache: path.join(root, 'cache') }, settings, secrets, registry, logger, ftp: { disconnect: async () => {} }, capabilities: { features: {} }, version: '1.0.9', }); const address = await app.listen(0, '127.0.0.1'); const base = `http://127.0.0.1:${address.port}`; const headers = { Host: `127.0.0.1:${address.port}`, Origin: base, 'Content-Type': 'application/json' }; try { const live = await fetch(`${base}/health/live`, { headers }); assert.equal(live.status, 200); const liveBody = await live.json(); assert.equal(liveBody.status, 'live'); assert.equal(typeof liveBody.build.id, 'string'); assert.equal(typeof liveBody.build.revision, 'string'); const initialSetupStatus = await fetch(`${base}/api/setup/status`, { headers }).then(response => response.json()); assert.equal(initialSetupStatus.defaultLanguage, 'en'); const setup = await fetch(`${base}/api/setup`, { method: 'POST', headers, body: JSON.stringify({ username: 'beheerder', password: ['veilig', 'wachtwoord', '123'].join('-'), allowedRoots: [root], safetyConfirmed: true, language: 'nl' }) }); assert.equal(setup.status, 201); assert.equal(operationsStarted, 1); const cookie = setup.headers.get('set-cookie').split(';')[0]; const auth = await fetch(`${base}/api/auth/status`, { headers: { ...headers, Cookie: cookie } }); const authBody = await auth.json(); assert.equal(authBody.authenticated, true); const shell = await fetch(`${base}/`, { headers: { ...headers, Cookie: cookie } }); assert.equal(shell.status, 200); assert.match(shell.headers.get('content-security-policy'), /script-src 'self'(?:;|$)/); assert.doesNotMatch(shell.headers.get('content-security-policy'), /script-src[^;]*unsafe-inline/); const sessionList = await fetch(`${base}/api/auth/sessions`, { headers: { ...headers, Cookie: cookie } }).then(response => response.json()); assert.equal(sessionList.sessions.length, 1); assert.equal(sessionList.sessions[0].current, true); assert.equal(sessionList.sessions[0].username, 'beheerder'); const commandCatalog = await fetch(`${base}/api/commands/catalog`, { headers: { ...headers, Cookie: cookie } }).then(response => response.json()); assert.equal(commandCatalog.schema, 1); assert.deepEqual(commandCatalog.commands.map(command => command.name), ['test:large', 'test:read', 'test:write']); assert.equal(commandCatalog.commands.find(command => command.name === 'test:read').classification, 'read-only'); assert.equal(commandCatalog.commands.find(command => command.name === 'test:large').maxBodyBytes, 512 * 1024); const rendererScript = await fetch(`${base}/modules/state.js`, { headers: { ...headers, Cookie: cookie } }); assert.equal(rendererScript.status, 200); assert.equal(rendererScript.headers.get('cache-control'), 'private, no-cache'); assert.ok(rendererScript.headers.get('etag')); const notModified = await fetch(`${base}/modules/state.js`, { headers: { ...headers, Cookie: cookie, 'If-None-Match': rendererScript.headers.get('etag') } }); assert.equal(notModified.status, 304); const denied = await fetch(`${base}/api/commands/test%3Awrite`, { method: 'POST', headers: { ...headers, Cookie: cookie }, body: JSON.stringify({ args: [{ ok: true }] }) }); assert.equal(denied.status, 403); assert.equal(denied.headers.get('cache-control'), 'no-store'); const readOnly = await fetch(`${base}/api/commands/test%3Aread`, { method: 'POST', headers: { ...headers, Cookie: cookie }, body: JSON.stringify({ args: [] }) }); assert.equal(readOnly.status, 200); assert.equal(readOnly.headers.get('cache-control'), 'no-store'); const allowed = await fetch(`${base}/api/commands/test%3Awrite`, { method: 'POST', headers: { ...headers, Cookie: cookie, 'X-CSRF-Token': authBody.csrfToken }, body: JSON.stringify({ args: [{ ok: true }] }) }); assert.equal(allowed.status, 200); assert.equal(allowed.headers.get('cache-control'), 'no-store'); const payload = 'x'.repeat(300 * 1024); const defaultLimited = await fetch(`${base}/api/commands/test%3Awrite`, { method: 'POST', headers: { ...headers, Cookie: cookie, 'X-CSRF-Token': authBody.csrfToken }, body: JSON.stringify({ args: [{ payload }] }) }); assert.equal(defaultLimited.status, 413); const explicitlyAllowed = await fetch(`${base}/api/commands/test%3Alarge`, { method: 'POST', headers: { ...headers, Cookie: cookie, 'X-CSRF-Token': authBody.csrfToken }, body: JSON.stringify({ args: [{ payload }] }) }); assert.equal(explicitlyAllowed.status, 200); assert.equal((await explicitlyAllowed.json()).result.length, payload.length); } finally { await app.close(); } }); test('metrics require a separate constant-time Bearer token', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-metrics-')); const settings = new SettingsStore(path.join(root, 'config', 'settings.json')); const secrets = new SecretsStore({ filePath: path.join(root, 'secrets', 'secrets.json'), keyFile: path.join(root, 'secrets', 'key') }); secrets.set('monitoring.token', 'a'.repeat(43)); const registry = new CommandRegistry({ platform: 'server' }); registry.register({ name: 'monitoring:prometheus', classification: 'read-only', permission: 'public', handler: () => 'chimera_up 1\n' }); const logger = { info() {}, warn() {}, error() {}, audit() {} }; const previousAllowedHosts = process.env.CHIMERA_ALLOWED_HOSTS; process.env.CHIMERA_ALLOWED_HOSTS = '127.0.0.1'; const app = createAppServer({ appRoot: path.resolve(__dirname, '../..'), paths: { root, cache: path.join(root, 'cache') }, settings, secrets, registry, logger, ftp: { disconnect: async () => {} }, capabilities: { features: {} }, version: '1.0.9', }); const address = await app.listen(0, '127.0.0.1'); const base = `http://127.0.0.1:${address.port}`; try { const denied = await fetch(`${base}/metrics`, { headers: { Host: `127.0.0.1:${address.port}`, Authorization: 'Bearer wrong' } }); assert.equal(denied.status, 401); const allowed = await fetch(`${base}/metrics`, { headers: { Host: `127.0.0.1:${address.port}`, Authorization: `Bearer ${'a'.repeat(43)}` } }); assert.equal(allowed.status, 200); assert.match(allowed.headers.get('content-type'), /version=0\.0\.4/); assert.equal(await allowed.text(), 'chimera_up 1\n'); } finally { await app.close(); fs.rmSync(root, { recursive: true, force: true }); if (previousAllowedHosts === undefined) delete process.env.CHIMERA_ALLOWED_HOSTS; else process.env.CHIMERA_ALLOWED_HOSTS = previousAllowedHosts; } }); test('authenticated Payload Manager proxy rewrites and isolates the PS5 web UI', { concurrency: false }, async () => { const payloadServer = http.createServer((req, res) => { if (req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(''); return; } if (req.url === '/list_payloads') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ payloads: ['/data/payloads/etaHEN.bin'] })); return; } res.writeHead(404).end(); }); await new Promise(resolve => payloadServer.listen(0, '127.0.0.1', resolve)); const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-payload-proxy-')); const settings = new SettingsStore(path.join(root, 'config', 'settings.json')); settings.set('ftp.host', '127.0.0.1'); settings.set('payloadManager.port', payloadServer.address().port); const secrets = new SecretsStore({ filePath: path.join(root, 'secrets', 'secrets.json'), keyFile: path.join(root, 'secrets', 'key') }); const registry = new CommandRegistry({ platform: 'server' }); const logger = { info() {}, warn() {}, error() {}, audit() {} }; const previousAllowedHosts = process.env.CHIMERA_ALLOWED_HOSTS; process.env.CHIMERA_ALLOWED_HOSTS = '127.0.0.1'; const app = createAppServer({ appRoot: path.resolve(__dirname, '../..'), paths: { root, cache: path.join(root, 'cache') }, settings, secrets, registry, logger, ftp: { disconnect: async () => {} }, capabilities: { features: {} }, version: '1.0.9', }); const address = await app.listen(0, '127.0.0.1'); const base = `http://127.0.0.1:${address.port}`; const headers = { Host: `127.0.0.1:${address.port}`, Origin: base, 'Content-Type': 'application/json' }; try { const setup = await fetch(`${base}/api/setup`, { method: 'POST', headers, body: JSON.stringify({ username: 'beheerder', password: ['veilig', 'wachtwoord', '123'].join('-'), allowedRoots: [root], safetyConfirmed: true }) }); assert.equal(setup.status, 201); const cookie = setup.headers.get('set-cookie').split(';')[0]; const entry = await fetch(`${base}/ps5-proxy/payload-manager/`, { redirect: 'manual', headers: { ...headers, Cookie: cookie } }); assert.equal(entry.status, 302); const tokenPath = entry.headers.get('location'); assert.match(tokenPath, /^\/ps5-proxy\/payload-manager\/t\/[A-Za-z0-9_-]{32,128}\/$/); const page = await fetch(`${base}${tokenPath}`, { headers: { Host: `127.0.0.1:${address.port}` } }); assert.equal(page.status, 200); assert.equal(page.headers.get('x-frame-options'), 'SAMEORIGIN'); assert.match(page.headers.get('content-security-policy'), /frame-ancestors 'self'/); const html = await page.text(); assert.match(html, new RegExp(`${tokenPath.replace(/\/$/, '')}/list_payloads`)); const listing = await fetch(`${base}${tokenPath}list_payloads`, { headers: { Host: `127.0.0.1:${address.port}`, Origin: 'null' } }); assert.equal(listing.status, 200); assert.equal(listing.headers.get('access-control-allow-origin'), 'null'); assert.deepEqual(await listing.json(), { payloads: ['/data/payloads/etaHEN.bin'] }); const blocked = await fetch(`${base}${tokenPath}shutdown`, { headers: { Host: `127.0.0.1:${address.port}`, Origin: 'null' } }); assert.equal(blocked.status, 404); } finally { await app.close(); await new Promise(resolve => payloadServer.close(resolve)); fs.rmSync(root, { recursive: true, force: true }); if (previousAllowedHosts === undefined) delete process.env.CHIMERA_ALLOWED_HOSTS; else process.env.CHIMERA_ALLOWED_HOSTS = previousAllowedHosts; } }); test('public Payload Manager repository is CORS-readable without exposing an authenticated command bridge', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-public-payloads-')); const settings = new SettingsStore(path.join(root, 'config', 'settings.json')); const secrets = new SecretsStore({ filePath: path.join(root, 'secrets', 'secrets.json'), keyFile: path.join(root, 'secrets', 'key') }); const registry = new CommandRegistry({ platform: 'server' }); registry.register({ name: 'ecosystem:payload-manager:repository', classification: 'read-only', permission: 'public', handler: () => ({ name: 'Chimera Verified Payloads', payloads: [{ name: 'ELF Loader', filename: 'elfldr.elf', url: 'https://example.test/elfldr.elf', checksum: 'a'.repeat(64) }] }) }); const logger = { info() {}, warn() {}, error() {}, audit() {} }; const previousAllowedHosts = process.env.CHIMERA_ALLOWED_HOSTS; process.env.CHIMERA_ALLOWED_HOSTS = '127.0.0.1'; const app = createAppServer({ appRoot: path.resolve(__dirname, '../..'), paths: { root, cache: path.join(root, 'cache') }, settings, secrets, registry, logger, ftp: { disconnect: async () => {} }, capabilities: { features: {} }, version: '1.0.9', }); const address = await app.listen(0, '127.0.0.1'); const base = `http://127.0.0.1:${address.port}`; try { const response = await fetch(`${base}/payloads.json`, { headers: { Host: `127.0.0.1:${address.port}`, Origin: 'http://ps5.local' } }); assert.equal(response.status, 200); assert.equal(response.headers.get('access-control-allow-origin'), '*'); assert.equal(response.headers.get('cross-origin-resource-policy'), 'cross-origin'); assert.match(response.headers.get('cache-control'), /max-age=300/); const repository = await response.json(); assert.equal(repository.payloads[0].checksum, 'a'.repeat(64)); const command = await fetch(`${base}/api/commands/ecosystem%3Apayload-manager%3Arepository`, { method: 'POST', headers: { Host: `127.0.0.1:${address.port}`, Origin: base, 'Content-Type': 'application/json' }, body: JSON.stringify({ args: [] }) }); assert.equal(command.status, 401); } finally { await app.close(); fs.rmSync(root, { recursive: true, force: true }); if (previousAllowedHosts === undefined) delete process.env.CHIMERA_ALLOWED_HOSTS; else process.env.CHIMERA_ALLOWED_HOSTS = previousAllowedHosts; } }); test('compiled Chimera Beacon artifact is public, CORS-readable and supports byte ranges', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-public-beacon-')); const appRoot = path.join(root, 'app'); fs.mkdirSync(path.join(appRoot, 'build', 'payloads'), { recursive: true }); const payload = Buffer.from([0x7f, 0x45, 0x4c, 0x46, 1, 2, 3, 4]); fs.writeFileSync(path.join(appRoot, 'build', 'payloads', 'chimera-beacon.elf'), payload); const settings = new SettingsStore(path.join(root, 'config', 'settings.json')); const secrets = new SecretsStore({ filePath: path.join(root, 'secrets', 'secrets.json'), keyFile: path.join(root, 'secrets', 'key') }); const registry = new CommandRegistry({ platform: 'server' }); const logger = { info() {}, warn() {}, error() {}, audit() {} }; const previousAllowedHosts = process.env.CHIMERA_ALLOWED_HOSTS; process.env.CHIMERA_ALLOWED_HOSTS = '127.0.0.1'; const app = createAppServer({ appRoot, paths: { root, cache: path.join(root, 'cache') }, settings, secrets, registry, logger, ftp: { disconnect: async () => {} }, capabilities: { features: {} }, version: '1.0.9' }); const address = await app.listen(0, '127.0.0.1'); try { const response = await fetch(`http://127.0.0.1:${address.port}/payloads/chimera-beacon.elf`, { headers: { Host: `127.0.0.1:${address.port}`, Range: 'bytes=0-3' } }); assert.equal(response.status, 206); assert.equal(response.headers.get('access-control-allow-origin'), '*'); assert.equal(response.headers.get('cross-origin-resource-policy'), 'cross-origin'); assert.equal(response.headers.get('content-type'), 'application/octet-stream'); assert.deepEqual(Buffer.from(await response.arrayBuffer()), payload.subarray(0, 4)); } finally { await app.close(); fs.rmSync(root, { recursive: true, force: true }); if (previousAllowedHosts === undefined) delete process.env.CHIMERA_ALLOWED_HOSTS; else process.env.CHIMERA_ALLOWED_HOSTS = previousAllowedHosts; } }); test('trusted reverse proxy enforces HTTPS, redacts setup details and serves byte ranges', { concurrency: false }, async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-https-')); const settings = new SettingsStore(path.join(root, 'config', 'settings.json')); settings.set('admin.username', 'beheerder'); settings.set('admin.passwordHash', 'configured'); const secrets = new SecretsStore({ filePath: path.join(root, 'secrets', 'secrets.json'), keyFile: path.join(root, 'secrets', 'key') }); const registry = new CommandRegistry({ platform: 'server' }); const logger = { info() {}, warn() {}, error() {}, audit() {} }; const previous = { allowedHosts: process.env.CHIMERA_ALLOWED_HOSTS, publicOrigin: process.env.CHIMERA_PUBLIC_ORIGIN, trustProxy: process.env.CHIMERA_TRUST_PROXY, cookieSecure: process.env.CHIMERA_COOKIE_SECURE, }; process.env.CHIMERA_ALLOWED_HOSTS = '127.0.0.1,chimera'; process.env.CHIMERA_PUBLIC_ORIGIN = 'https://chimera.test:18443'; process.env.CHIMERA_TRUST_PROXY = '1'; process.env.CHIMERA_COOKIE_SECURE = '1'; const app = createAppServer({ appRoot: path.resolve(__dirname, '../..'), paths: { root, cache: path.join(root, 'cache') }, settings, secrets, registry, logger, ftp: { disconnect: async () => {} }, capabilities: { features: {} }, version: '1.0.9', }); const address = await app.listen(0, '127.0.0.1'); const base = `http://127.0.0.1:${address.port}`; const proxied = { Host: 'chimera.test:18443', 'X-Forwarded-Proto': 'https', 'X-Forwarded-For': '192.168.1.171' }; try { const direct = await fetch(`${base}/server-login.html`, { redirect: 'manual', headers: { Host: `127.0.0.1:${address.port}` } }); assert.equal(direct.status, 308); assert.equal(direct.headers.get('location'), 'https://chimera.test:18443/server-login.html'); const login = await fetch(`${base}/server-login.html`, { headers: proxied }); assert.equal(login.status, 200); assert.match(login.headers.get('strict-transport-security'), /max-age=31536000/); assert.equal(login.headers.get('x-frame-options'), 'DENY'); const status = await fetch(`${base}/api/setup/status`, { headers: proxied }).then(response => response.json()); assert.deepEqual(status, { configured: true, defaultLanguage: 'en' }); const icon = await fetch(`${base}/build/icon.png`, { headers: { ...proxied, Range: 'bytes=0-9' } }); assert.equal(icon.status, 206); assert.equal(icon.headers.get('content-length'), '10'); assert.match(icon.headers.get('content-range'), /^bytes 0-9\//); assert.equal((await icon.arrayBuffer()).byteLength, 10); } finally { await app.close(); for (const [key, value] of Object.entries({ CHIMERA_ALLOWED_HOSTS: previous.allowedHosts, CHIMERA_PUBLIC_ORIGIN: previous.publicOrigin, CHIMERA_TRUST_PROXY: previous.trustProxy, CHIMERA_COOKIE_SECURE: previous.cookieSecure })) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } } }); test('Remote Play worker endpoint bypasses browser sessions only through its dedicated authenticator', { concurrency: false }, async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-remote-worker-http-')); const settings = new SettingsStore(path.join(root, 'config', 'settings.json')); const secrets = new SecretsStore({ filePath: path.join(root, 'secrets', 'secrets.json'), keyFile: path.join(root, 'secrets', 'key') }); const registry = new CommandRegistry({ platform: 'server' }); const calls = []; registry.remotePlay = { prefix: '/api/remote-play-worker/v1', handleWorkerRequest(pathname, body) { calls.push({ pathname, body }); if (body.token !== 'signed-fixture') throw Object.assign(new Error('Worker-HMAC is ongeldig.'), { statusCode: 401 }); return { ok: true, workerId: 'rpw-fixture' }; }, }; const logger = { info() {}, warn() {}, error() {}, audit() {} }; const previousAllowedHosts = process.env.CHIMERA_ALLOWED_HOSTS; process.env.CHIMERA_ALLOWED_HOSTS = '127.0.0.1'; const app = createAppServer({ appRoot: path.resolve(__dirname, '../..'), paths: { root, cache: path.join(root, 'cache') }, settings, secrets, registry, logger, ftp: { disconnect: async () => {} }, capabilities: { features: {} }, version: '1.0.9' }); const address = await app.listen(0, '127.0.0.1'); const base = `http://127.0.0.1:${address.port}`; const headers = { Host: `127.0.0.1:${address.port}`, 'Content-Type': 'application/json' }; try { const denied = await fetch(`${base}/api/remote-play-worker/v1/poll`, { method: 'POST', headers, body: JSON.stringify({ token: 'wrong' }) }); assert.equal(denied.status, 401); assert.equal(denied.headers.get('cache-control'), 'no-store'); const accepted = await fetch(`${base}/api/remote-play-worker/v1/poll`, { method: 'POST', headers, body: JSON.stringify({ token: 'signed-fixture' }) }); assert.equal(accepted.status, 200); assert.equal(accepted.headers.get('cache-control'), 'no-store'); assert.deepEqual(await accepted.json(), { ok: true, workerId: 'rpw-fixture' }); const normalApi = await fetch(`${base}/api/capabilities`, { headers }); assert.equal(normalApi.status, 401); assert.equal(calls.length, 2); } finally { await app.close(); fs.rmSync(root, { recursive: true, force: true }); if (previousAllowedHosts === undefined) delete process.env.CHIMERA_ALLOWED_HOSTS; else process.env.CHIMERA_ALLOWED_HOSTS = previousAllowedHosts; } });