import test from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { parseArgs, addresses, connectionEnv, readSettings } from './dev-all.mjs'; test('default and named host/port options', () => { assert.deepEqual(parseArgs([]), { host: '127.0.0.1', port: 3000 }); assert.deepEqual(parseArgs(['--host', '0.0.0.0', '--port', '3002']), { host: '0.0.0.0', port: 3002 }); assert.deepEqual(parseArgs(['--port=3002', '--host=localhost']), { host: '127.0.0.1', port: 3002 }); for (const args of [['--port', '0'], ['--port', '65536'], ['--port', '3abc'], ['--host', 'evil.example'], ['--host'], ['—port', '3002'], ['--unknown']]) assert.throws(() => parseArgs(args)); }); const networks = { en0: [{ address: '192.168.1.23', family: 'IPv4', internal: false }], other: [{ address: '8.8.8.8', family: 'IPv4', internal: false }], lo: [{ address: '127.0.0.1', family: 'IPv4', internal: true }] }; test('LAN listener keeps camera-capable loopback and explicit LAN origins', () => { const result = addresses({ host: '0.0.0.0', port: 3002 }, networks, {}); assert.equal(result.origin, 'http://192.168.1.23:3002'); assert.equal(result.local, 'http://127.0.0.1:3002'); assert.deepEqual(result.allowed.sort(), ['http://192.168.1.23:3002', 'http://127.0.0.1:3002', 'http://localhost:3002'].sort()); assert.ok(!result.allowed.some(origin => origin.includes('0.0.0.0') || origin.includes('8.8.8.8'))); }); test('loopback default and IPv6 formatting', () => { assert.equal(addresses(parseArgs([]), networks, {}).origin, 'http://localhost:3000'); assert.deepEqual(addresses({ host: '::1', port: 3002 }, {}, {}).allowed, ['http://localhost:3002', 'http://[::1]:3002']); }); test('explicit HTTPS origin does not silently authorize HTTP aliases', () => { const result = addresses({ host: '0.0.0.0', port: 3002 }, networks, { WEBKA_ORIGIN: 'https://webka.local' }); assert.deepEqual(result.allowed, ['https://webka.local']); assert.throws(() => addresses(parseArgs([]), {}, { WEBKA_ORIGIN: 'https://webka.local/login' })); assert.throws(() => addresses(parseArgs([]), {}, { WEBKA_ALLOWED_ORIGINS: '*' })); }); test('homepage mode accepts proxy domain/IP, normalizes HTTPS and exposes the upstream', () => { assert.deepEqual(parseArgs(['--homepage', 'camera.example.com', '--port', '3002']), { host: '0.0.0.0', port: 3002, homepage: 'https://camera.example.com' }); assert.deepEqual(parseArgs(['--homepage=https://192.168.1.10:8443/', '--host=127.0.0.1']), { host: '127.0.0.1', port: 3000, homepage: 'https://192.168.1.10:8443' }); for (const address of ['', 'http://camera.example.com', 'https://camera.example.com/watch', 'https://a:b@camera.example.com', 'https://camera.example.com?x=1', 'https://camera.example.com#x', 'https://0.0.0.0', 'https://[::]']) assert.throws(() => parseArgs(['--homepage', address])); }); test('saved settings apply, with command-line overrides', () => { const settings = { homepage: 'https://camera.example.com', port: 3002 }; assert.deepEqual(parseArgs([], settings), { host: '0.0.0.0', port: 3002, homepage: settings.homepage }); assert.deepEqual(parseArgs(['--homepage', 'https://other.example.com', '--port', '3003', '--host', '127.0.0.1'], settings), { host: '127.0.0.1', port: 3003, homepage: 'https://other.example.com' }); assert.throws(() => parseArgs([], { port: 'invalid' })); }); test('configuration file loads a homepage and rejects malformed or misspelled settings', () => { const directory = mkdtempSync(join(tmpdir(), 'webka-settings-')); const path = join(directory, 'webka.config.json'); try { assert.deepEqual(readSettings(path), {}); writeFileSync(path, JSON.stringify({ homepage: 'camera.example.com', port: 3002 })); assert.deepEqual(parseArgs([], readSettings(path)), { host: '0.0.0.0', port: 3002, homepage: 'https://camera.example.com' }); for (const invalid of ['{', 'null', '[]', '{"homePage":"camera.example.com"}']) { writeFileSync(path, invalid); assert.throws(() => readSettings(path), /webka.config.json/); } } finally { rmSync(directory, { recursive: true, force: true }); } }); test('homepage wins over old HTTPS/mDNS environment settings without allowing HTTP aliases', () => { const env = { WEBKA_MDNS: '1', WEBKA_ORIGIN: 'https://old.local', WEBKA_ALLOWED_ORIGINS: 'http://unsafe.example.com' }; const config = parseArgs(['--homepage', 'https://camera.example.com:8443', '--port', '3002']); const urls = addresses(config, networks, env); assert.equal(urls.origin, config.homepage); assert.deepEqual(urls.allowed, [config.homepage]); assert.deepEqual(connectionEnv(config, urls, env), { WEBKA_BIND: '0.0.0.0:3002', WEBKA_ORIGIN: config.homepage, WEBKA_ALLOWED_ORIGINS: config.homepage, WEBKA_MDNS: '0', WEBKA_PUBLIC_PORT: '8443' }); });