import test from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, readFileSync, writeFileSync, chmodSync, symlinkSync, rmSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { inspectInterpreter, recordInterpreterStart } from '../src/interpreter.mjs'; function fixture(change = {}) { const binary = Buffer.from('\x7fELFbody'); return { realpath: () => '/usr/bin/python3.14', stat: path => ({ uid: 0, mode: 0o100755, dev: 1, ino: 10, isFile: () => path.endsWith('3.14'), isDirectory: () => !path.endsWith('3.14') }), readFile: path => path === '/etc/passwd' ? 'root:x:0:0::/root:/bin/sh\nuser:x:1000:1000::/home/user:/bin/sh\n' : binary, exec: () => '[3,14,0]', ...change }; } test('system Python is a resolved root-owned ELF, never a shim or home interpreter', () => { const record = inspectInterpreter({ io: fixture() }); assert.equal(record.path, '/usr/bin/python3.14'); assert.deepEqual(record.version, [3,14,0]); assert.match(record.sha256, /^[a-f0-9]{64}$/); const bad = [ { realpath: () => '/home/user/python3' }, { realpath: () => '/root/python3' }, { realpath: () => '/srv/user/python3', readFile: p => p === '/etc/passwd' ? 'u:x:1000:1000::/srv/user:/bin/sh' : Buffer.from('\x7fELF') }, { readFile: p => p === '/etc/passwd' ? '' : Buffer.from('#!/bin/sh\n') }, { stat: () => ({ uid: 1000, mode: 0o100755, isFile: () => true, isDirectory: () => true }) }, { stat: () => ({ uid: 0, mode: 0o100775, isFile: () => true, isDirectory: () => true }) }, { stat: () => { throw Error('missing'); } }, { exec: () => '[3,9,9]' }, { exec: () => 'unexpected' }, ]; for (const f of bad) assert.throws(() => inspectInterpreter({ io: fixture(f) }), /interpreter-unsafe/); }); test('real system interpreter is available and safely inspected', () => { const actual = inspectInterpreter(); assert.ok(actual.path.startsWith('/usr/')); assert.ok(actual.version[0] >= 3); }); test('restart receipt appends actual replacement identity and refuses unsafe files', t => { const root = mkdtempSync(join(tmpdir(), 'g196-int-')); t.after(() => rmSync(root, { recursive: true, force: true })); const first = inspectInterpreter({ io: fixture() }); recordInterpreterStart(root, first); const file = join(root, 'peercred-starts.jsonl'); const old = readFileSync(file, 'utf8'); const second = inspectInterpreter({ io: fixture({ readFile: p => p === '/etc/passwd' ? '' : Buffer.from('\x7fELFnew') }) }); recordInterpreterStart(root, second); const now = readFileSync(file, 'utf8'); assert.ok(now.startsWith(old)); assert.notEqual(JSON.parse(old).sha256, JSON.parse(now.trim().split('\n')[1]).sha256); chmodSync(file, 0o644); assert.throws(() => recordInterpreterStart(root, second), /interpreter-receipt-unsafe/); rmSync(file); writeFileSync(join(root,'target'), 'untouched'); symlinkSync(join(root,'target'),file); assert.throws(() => recordInterpreterStart(root, second), /interpreter-receipt-unsafe/); assert.equal(readFileSync(join(root,'target'),'utf8'),'untouched'); }); test('activation gate compares fresh startup receipt to the current interpreter identity',async t=>{ const { verifyInterpreterReceipt }=await import('../src/interpreter.mjs'); const root=mkdtempSync(join(tmpdir(),'g196-int-'));t.after(()=>rmSync(root,{recursive:true,force:true})); const first=inspectInterpreter({io:fixture()}),since=Date.now()-1; recordInterpreterStart(root,first); assert.equal(verifyInterpreterReceipt(root,first,since),true); assert.throws(()=>verifyInterpreterReceipt(root,{...first,sha256:'b'.repeat(64)},since),/interpreter-receipt-mismatch/); assert.throws(()=>verifyInterpreterReceipt(root,first,Date.now()+1000),/interpreter-receipt-mismatch/); }); test('non-regular startup receipt refuses promptly without waiting on a FIFO peer',async t=>{ const {execFileSync,spawnSync}=await import('node:child_process'); const root=mkdtempSync(join(tmpdir(),'g196-fifo-'));t.after(()=>rmSync(root,{recursive:true,force:true})); execFileSync('mkfifo',[join(root,'peercred-starts.jsonl')]); const url=new URL('../src/interpreter.mjs',import.meta.url).href; const child=spawnSync(process.execPath,['--input-type=module','-e',`import {recordInterpreterStart} from ${JSON.stringify(url)};try{recordInterpreterStart(${JSON.stringify(root)},{});process.exit(2)}catch(e){process.exit(e.message==='interpreter-receipt-unsafe'?0:3)}`],{timeout:1000}); assert.equal(child.status,0,'unsafe FIFO must refuse without blocking startup'); }); //641: scheduler delay is lack of probe evidence, not evidence of an unsafe executable. test('interpreter probe timeout refuses distinctly without approving the interpreter', () => { let calls = 0; assert.throws(() => inspectInterpreter({ io: fixture({ exec: (_path, _args, options) => { calls++; assert.equal(options.timeout, 1000); throw Object.assign(Error('controlled timeout'), { code: 'ETIMEDOUT' }); } }) }), { message: 'interpreter-probe-timeout' }); assert.equal(calls, 1); });