Files
stack/packages/bus/tests/process.test.mjs
jason.woltje 6096b051b1 row 243: tests that depend on the gate umask set their file modes; stack-instances bootstraps under an explicit umask with a 077 subtest (#1754)
process.test, proof-cli.test and sso.test set the too-open mode with chmod
instead of taking it from the umask. stack-instances.test bootstraps under an
explicit umask and asserts the 0700 config dir under umask 077.
Candidate morgana/row243 6581f750e (code 137bda3af), Webby APPROVE rev 2939.
2026-10-21 14:12:03 -05:00

216 lines
9.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { fork } from 'node:child_process';
import { mkdtempSync, rmSync, existsSync, writeFileSync, mkdirSync, readFileSync, chmodSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { once } from 'node:events';
import { Client } from '../src/client.mjs';
const businesses = {
demo: {
id: 'demo',
human: 'jason',
// Row 65: runtime launches bind only on a harness the business switches on.
plugins: { harness: ['pi'] },
arbiters: { technical: 'cto', delivery: 'cto' },
roles: { cto: { authority: { withinRole: ['message.send'], crossRole: [] } } },
},
};
function launch(t) {
const child = fork(new URL('../src/process.mjs', import.meta.url), [], {
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
});
let logs = '';
child.stdout.on('data', (b) => (logs += b));
child.stderr.on('data', (b) => (logs += b));
t.after(() => {
if (child.exitCode === null) child.kill('SIGKILL');
});
return { child, logs: () => logs };
}
async function boot(child, config) {
const reply = Promise.race([
once(child, 'message'),
once(child, 'exit').then(() => {
throw Error('exited-before-reply');
}),
]);
child.send({ op: 'boot', config });
return (await reply)[0];
}
test('broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'bus-process-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const { child } = launch(t);
const ready = await boot(child, {
dataRoot: root,
businesses,
launches: [{ business: 'demo', role: 'cto', run: 'r1', harness: 'pi', pid: process.pid, startTime: '1' }],
readers: ['demo'],
});
assert.equal(ready.ok, true);
const agent = new Client({ path: ready.path, cap: ready.launches[0].cap });
await agent.call('role.claim');
const reader = new Client({ path: ready.path, cap: ready.readers[0].cap });
assert.equal((await reader.call('agents'))[0].holder_run, 'r1');
await assert.rejects(reader.call('launch.revoke'), /read-only/);
await assert.rejects(
new Client({
path: ready.path,
human: { business: 'demo', pid: process.pid, startTime: '1', nonce: 'f'.repeat(64) },
}).call('launch.revoke'),
/human-required/,
);
const exit = once(child, 'exit');
child.send({ op: 'close' });
assert.equal((await exit)[0], 0);
assert.equal(existsSync(join(root, 'bus/writer.lock')), false);
});
test('startup token refusal returns safe code without value or partial listening broker', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'bus-boot-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const data = join(root, 'data');
mkdirSync(data);
const secret = 'fixture-token-never-in-db-948723';
const file = join(root, 'secret');
writeFileSync(file, secret, { mode: 0o644 });
// writeFileSync applies the umask; chmod sets the too-open mode under any umask (row 243).
chmodSync(file, 0o644);
const config = structuredClone(businesses);
config.demo.roles.cto.credentials = { gitea: { file, rotateBy: '2099-01-01' } };
const { child, logs } = launch(t);
const exit = once(child, 'exit');
const ready = await boot(child, { dataRoot: data, businesses: config, launches: [], readers: [] });
assert.equal(ready.ok, false);
assert.equal(ready.error, 'credential-file');
assert.equal((await exit)[0], 2);
assert.ok(!logs().includes(secret));
assert.equal(existsSync(join(data, 'bus/broker.sock')), false);
});
test('loaded fixture token is absent from socket replies and SQLite, including refusal evidence', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'bus-secret-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const data = join(root, 'data');
mkdirSync(data);
const token = 'fixture-opaque-token-e9c39140';
const file = join(root, 'token');
writeFileSync(file, token, { mode: 0o600 });
const config = structuredClone(businesses);
config.demo.roles.cto.credentials = { gitea: { file, rotateBy: '2099-01-01' } };
const { child, logs } = launch(t);
const ready = await boot(child, {
dataRoot: data,
businesses: config,
launches: [{ business: 'demo', role: 'cto', run: 'r2', harness: 'pi', pid: process.pid, startTime: '1' }],
});
assert.equal(ready.ok, true);
const c = new Client({ path: ready.path, cap: ready.launches[0].cap });
await c.call('role.claim');
await assert.rejects(c.call('message.send', { to: 'cto', body: token }), /credential-leak/);
const exit = once(child, 'exit');
child.send({ op: 'close' });
await exit;
assert.ok(!readFileSync(join(data, 'bus/bus.sqlite')).includes(Buffer.from(token)));
assert.ok(!logs().includes(token));
});
test('killed broker leaves an explicit stale lock; another process cannot silently reclaim it', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'bus-crash-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const first = launch(t);
const config = { dataRoot: root, businesses, launches: [] };
assert.equal((await boot(first.child, config)).ok, true);
const killed = once(first.child, 'exit');
first.child.kill('SIGKILL');
await killed;
assert.equal(existsSync(join(root, 'bus/writer.lock')), true);
const second = launch(t),
ended = once(second.child, 'exit');
const refusal = await boot(second.child, config);
assert.equal(refusal.ok, false);
assert.equal((await ended)[0], 2);
assert.equal(existsSync(join(root, 'bus/writer.lock')), true);
});
test('trusted host registers later launches; socket clients never have a registration verb', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'bus-add-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const { child } = launch(t);
const ready = await boot(child, { dataRoot: root, businesses, launches: [] });
assert.equal(ready.ok, true);
const message = once(child, 'message');
child.send({
op: 'bindLaunch',
record: { business: 'demo', role: 'cto', run: 'later', harness: 'pi', pid: process.pid, startTime: '1' },
});
const bound = (await message)[0];
assert.equal(bound.ok, true);
const c = new Client({ path: ready.path, cap: bound.launch.cap });
await c.call('role.claim');
await assert.rejects(c.call('bindLaunch', { role: 'cto' }), /unknown-verb/);
const exit = once(child, 'exit');
child.send({ op: 'close' });
assert.equal((await exit)[0], 0);
});
test('REQ-LIMIT-4 the broker process answers the launcher\'s mayLaunch op with the limits\' awaited decision', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'bus-may-launch-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const { child } = launch(t);
const ready = await boot(child, { dataRoot: root, businesses, launches: [] });
assert.equal(ready.ok, true);
const ask = async (m) => {
const message = once(child, 'message');
child.send(m);
return (await message)[0];
};
// No limit check is switched on for demo, so the launch is allowed.
assert.deepEqual(await ask({ op: 'mayLaunch', id: 7, business: 'demo', instance: 'cto', provider: 'p', model: 'm' }),
{ ok: true, result: { allow: true }, id: 7 });
const exit = once(child, 'exit');
child.send({ op: 'close' });
assert.equal((await exit)[0], 0);
});
test('runtime excludes declared project roots even when host supplies no repoRoots', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'bus-project-token-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const data = join(root, 'data');
mkdirSync(data);
const project = join(root, 'project');
mkdirSync(project);
const file = join(project, 'token');
writeFileSync(file, 'fixture-do-not-load-from-project', { mode: 0o600 });
const config = structuredClone(businesses);
config.demo.projects = { stack: { root: project } };
config.demo.roles.cto.credentials = { gitea: { file, rotateBy: '2099-01-01' } };
const { child } = launch(t),
exit = once(child, 'exit');
const result = await boot(child, { dataRoot: data, businesses: config });
assert.equal(result.ok, false);
assert.equal(result.error, 'credential-location');
assert.equal((await exit)[0], 2);
});
test('a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'bus-bind-refusal-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const { child } = launch(t);
const record = {
business: 'demo',
role: 'cto',
run: 'one',
harness: 'pi',
pid: process.pid,
startTime: '1',
};
const ready = await boot(child, { dataRoot: root, businesses, launches: [record] });
assert.equal(ready.ok, true);
const client = new Client({ path: ready.path, cap: ready.launches[0].cap });
await client.call('role.claim');
const reply = once(child, 'message');
child.send({ op: 'bindLaunch', record });
assert.deepEqual((await reply)[0], { ok: false, error: 'duplicate-run' });
assert.equal((await client.call('agents'))[0].holder_run, 'one');
const bad = once(child, 'message'),
exit = once(child, 'exit');
child.send({ op: 'not-a-protocol-verb' });
assert.equal((await bad)[0].ok, false);
assert.equal((await exit)[0], 2);
});