Candidate527b9a1e6(gander/row196-r6-prd19, basef48725cfc), the 39 manifest paths of CANDIDATE.sha256 537d30aef70b6cd1 atec1e7fad8. 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.
47 lines
2.7 KiB
JavaScript
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) });
|
|
}
|