Files

72 lines
5.1 KiB
JavaScript

// P4 named mutant: API key echoed in a response, error, log or durable record.
// Real HTTP -> human capability -> broker -> page -> management client -> fake.
import test from 'node:test';
import assert from 'node:assert/strict';
import { request } from 'node:http';
import { mkdtempSync, readdirSync, readFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fakeManagement } from '../../bus/tests/proxy-page-fixture.mjs';
import { Store } from '../../bus/src/store.mjs';
import { Broker } from '../../bus/src/broker.mjs';
import { startServer } from '../src/serve.mjs';
import { createSsoSession } from './helpers/sso-session.mjs';
const scan = root => readdirSync(root, { withFileTypes: true }).flatMap(e => e.isDirectory() ? scan(join(root, e.name)) : [readFileSync(join(root, e.name))]);
test('API-key canary reaches fake once and zero responses/logs/receipts/store files', async t => {
const canary = 'p4-canary-key';
const fake = await fakeManagement(t);
const root = mkdtempSync(join(tmpdir(), 'p4-canary-'));
const store = new Store(root);
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
const b = new Broker({ store, proxyPage: (...args) => fake.page.request(...args),
businesses: { demo: { id: 'demo', human: 'owner', roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { authority: { withinRole: [], crossRole: [] } } }, arbiters: { technical: 'chief', delivery: 'chief' } } } });
const cap = b.bindHuman({ business: 'demo', human: 'owner', via: 'cli', outsideAgent: true });
const logs = [], output = [];
const auth = await createSsoSession(t, { host: 'console.example', owner: { sub: 'owner-sub', name: 'owner' } });
const server = await startServer({ port: 0, sso: auth.sso,
send: async () => ({}), proxyCall: (verb, args) => b.request(cap, { verb, args }), log: line => logs.push(line) });
t.after(() => new Promise(r => server.close(r)));
const session = await auth.signIn(server.address().port);
const headers = auth.writeHeaders(session);
const call = (path, data) => new Promise((resolve, reject) => {
const req = request({ hostname: '127.0.0.1', port: server.address().port, path, method: data ? 'POST' : 'GET',
headers }, res => {
let text = ''; res.on('data', c => text += c); res.on('end', () => { output.push(text); resolve(res.statusCode); });
}); req.on('error', reject); req.end(data ? JSON.stringify(data) : undefined);
});
// Capture process output during the operation as well as structured log
// callbacks. Even a deliberate log-leak mutant must never print its canary.
const stdout = process.stdout.write, stderr = process.stderr.write;
const capture = (chunk, ...rest) => { output.push(Buffer.from(chunk)); const cb = rest.at(-1); if (typeof cb === 'function') cb(); return true; };
let added, accounts;
try {
process.stdout.write = capture; process.stderr.write = capture;
added = await call('/api/proxy/apikey/add', { provider: 'z.ai', key: canary });
accounts = await call('/api/proxy/accounts');
} finally { process.stdout.write = stdout; process.stderr.write = stderr; }
assert.equal(added, 200); assert.equal(accounts, 200);
assert.equal(fake.state.receives, 1);
assert.equal(fake.state.groups[0].keys[0]['api-key'] === canary, true);
for (const bytes of [...output, ...logs, JSON.stringify(fake.state.receipts), ...scan(root)])
assert.equal(Buffer.from(bytes).includes(Buffer.from(canary)), false, 'credential value escaped');
});
test('OAuth URLs, state and code stay out of broker records and management receipts', async t => {
const fake = await fakeManagement(t);
const root = mkdtempSync(join(tmpdir(), 'p4-oauth-'));
const store = new Store(root);
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
const b = new Broker({ store, proxyPage: (...args) => fake.page.request(...args),
businesses: { demo: { id: 'demo', human: 'owner', roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { authority: { withinRole: [], crossRole: [] } } }, arbiters: { technical: 'chief', delivery: 'chief' } } } });
const cap = b.bindHuman({ business: 'demo', human: 'owner', via: 'cli', outsideAgent: true });
const call = (verb, args = {}) => b.request(cap, { verb, args });
const login = await call('proxy.login.start', { provider: 'claude' });
const callback = 'http://localhost/callback?state=' + login.state + '&code=p4-private-code';
assert.deepEqual(await call('proxy.login.submit', { redirect_url: callback }), { status: 'wait' });
assert.deepEqual(await call('proxy.login.status', { state: login.state }), { status: 'wait' });
for (const value of [login.url, login.state, callback, 'p4-private-code']) {
assert.equal(JSON.stringify(fake.state.receipts).includes(value), false);
for (const bytes of scan(root)) assert.equal(bytes.includes(Buffer.from(value)), false);
}
});