Files
stack/packages/webui/src/tester-socket.mjs
T
jason.woltje 7247d80c7a row 196: human boundary on peer credentials, interpreter receipt and the Console tester socket (#1694)
Candidate 527b9a1e6 (gander/row196-r6-prd19, base f48725cfc), the 39 manifest paths of CANDIDATE.sha256 537d30aef70b6cd1 at ec1e7fad8. Duckworth approved round 1 outside B1/B2 (rev 2973, comment 30145) and round 2 merge-scoped (rev 2986, comment 30156). Gate gander-0066 on grant sage-2145: 2334 pass, 0 fail, 1 skip; mutants 38/38 killed. BUILD-LOG lines and Duckworth's C1 DEFERRED entry follow as a doc-only append.
2026-10-21 15:55:39 -05:00

47 lines
2.7 KiB
JavaScript

import { lstatSync, mkdirSync, chmodSync, unlinkSync, renameSync } from 'node:fs';
import { isAbsolute, join, dirname } from 'node:path';
import { randomBytes } from 'node:crypto';
export const TESTER_ORIGIN = 'http://mosaic-tester.invalid';
export function testerSocketPath(dataRoot) {
if (typeof dataRoot !== 'string' || !isAbsolute(dataRoot)) throw Error('tester-socket-path');
const path = join(dataRoot, 'console', 'tester.sock');
if (Buffer.byteLength(path) > 107) throw Error('tester-socket-path');
return path;
}
export async function bindTesterSocket(server, path) {
if (typeof path !== 'string' || !isAbsolute(path) || Buffer.byteLength(path) > 107) throw Error('tester-socket-path');
const parent = dirname(path);
try { mkdirSync(parent, { mode: 0o700 }); } catch (e) { if (e.code !== 'EEXIST') throw Error('tester-socket-unsafe'); }
const dir = lstatSync(parent);
if (!dir.isDirectory() || dir.isSymbolicLink() || dir.uid !== process.getuid() || (dir.mode & 0o077)) throw Error('tester-socket-unsafe');
try { lstatSync(path); throw Error('tester-socket-exists'); } catch (e) { if (e.code !== 'ENOENT') throw e; }
// Same length as tester.sock, so the final-path capacity check covers the temporary name.
const temporary = join(parent, '.' + randomBytes(4).toString('hex') + '.s');
try { lstatSync(temporary); throw Error('tester-socket-exists'); } catch (e) { if (e.code !== 'ENOENT') throw e; }
await new Promise((resolve, reject) => {
server.once('error', reject);
server.listen(temporary, () => { server.off('error', reject); resolve(); });
});
try {
chmodSync(temporary, 0o600);
const owned = lstatSync(temporary);
if (!owned.isSocket() || owned.uid !== process.getuid() || (owned.mode & 0o777) !== 0o600) throw Error('tester-socket-unsafe');
// Recheck before publishing. Same-uid swaps between lstat and rename/unlink remain
// outside the boundary, as Sage's option-1 ruling and the build receipt state.
try { lstatSync(path); throw Error('tester-socket-exists'); } catch (e) { if (e.code !== 'ENOENT') throw e; }
renameSync(temporary, path);
// libuv remembers only the now-absent temporary name. Publish the actual client path.
server.address = () => server.listening ? path : null;
server.once('close', () => {
try { const s = lstatSync(path); if (s.dev === owned.dev && s.ino === owned.ino) unlinkSync(path); }
catch (e) { if (e.code !== 'ENOENT') server.emit('error', e); }
});
return server;
} catch (error) { await new Promise(r => server.close(r)); throw error; }
}
export async function startTesterSocket({ dataRoot, ...options }) {
const { startServer } = await import('./serve.mjs');
return startServer({ ...options, tester: true, testerSocket: testerSocketPath(dataRoot) });
}