Files
jason.woltje e69260a3ce row 244: gate runs mount a 0600 fixture pi auth file, test-release activates on the mock adapter, and real-model test-task cases sit behind MOSAIC_LIVE_PROVIDER=1 (#1757)
Candidate morgana/row244 94ab4c2fe on a36523713, manifest 6e7e3e3c (8 paths).
Webby APPROVE r1, rev 3008 (webby-0084, REVIEW-r1.md at webby/reviews
ebb3d83bf; request #1757 c30154). Gate on Sage's slot at 81e037d20
(sage-2147): the five gate-lane files whole 51/0, test-task.sh 91/0,
test-release.sh 14/0, mutants M-G, M-L, M-A, M-T and M-F killed.
94ab4c2fe adds only BUILD.md results. Webby's drift check: a36523713..d37210288
changes none of the 8 paths and no path either suite reads; merge-tree clean.
Rulings A and C (sage-1914, sage-2061). The dropped real-model coverage goes
to DEFERRED in the follow-up doc commit.
2026-10-21 16:44:37 -05:00

69 lines
4.4 KiB
JavaScript

import { execFileSync, spawnSync } from 'node:child_process';
import { readFileSync, writeFileSync, linkSync, unlinkSync, openSync, closeSync, fstatSync, realpathSync, statSync, constants } from 'node:fs';
import { join, isAbsolute } from 'node:path';
import { pathToFileURL } from 'node:url';
import { refuse, shortRoot, nonce, within } from './lane-state.mjs';
const HOST_PI_AUTH='/home/jwoltje/.pi/agent/auth.json';
function composeFiles(run){
if(!/^sha256:[a-f0-9]{64}$/.test(run.imageId??''))refuse('invalid pinned image ID');
if(typeof run.checkout!=='string'||!isAbsolute(run.checkout))refuse('invalid candidate checkout');
return [join(run.checkout,'compose.yaml'),join(shortRoot(run.tempRoot),'compose-no-build.yaml')];
}
export function writeComposeOverride(run){
const [,file]=composeFiles(run);
const text=`services:\n mosaic-agent:\n build: !reset null\n image: ${JSON.stringify(run.imageId)}\n`;
// Publish a complete file without replacing another guard's override.
const pending=file+'.'+nonce();
try{
writeFileSync(pending,text,{mode:0o600,flag:'wx'});
try{linkSync(pending,file);}catch(e){if(e.code!=='EEXIST')throw e;}
}finally{try{unlinkSync(pending);}catch(e){if(e.code!=='ENOENT')throw e;}}
let fd;
try{
fd=openSync(file,constants.O_RDONLY|constants.O_NOFOLLOW|constants.O_NONBLOCK);
const st=fstatSync(fd);
if(!st.isFile()||st.uid!==process.getuid()||(st.mode&0o777)!==0o600||st.size!==Buffer.byteLength(text)||readFileSync(fd,'utf8')!==text)
refuse('unsafe or changed Compose override');
}catch{refuse('unsafe or changed Compose override');}
finally{if(fd!==undefined)closeSync(fd);}
return file;
}
// compose.yaml mounts PI_AUTH_FILE, defaulting to the host's real pi auth file (row 244).
function laneAuthFile(env,run){
const file=env.PI_AUTH_FILE;let real,root;
try{real=realpathSync(file);root=realpathSync(run.tempRoot);}catch{refuse('Compose auth mount not a lane fixture');}
if(!isAbsolute(file)||file===HOST_PI_AUTH||real===HOST_PI_AUTH||!within(root,real)||!statSync(real).isFile())refuse('Compose auth mount not a lane fixture');
}
export function guardDocker(argv,run,env=process.env){
if(argv[0]==='info'&&argv.length===1)return argv;
if(argv[0]==='image'&&argv[1]==='inspect'&&argv[2]===run.tag&&argv.length===3)return argv;
if(argv[0]==='compose'&&argv[1]==='run'){
const service=argv.indexOf('mosaic-agent',2);
if(service<0||argv.slice(2,service).some(a=>!['--rm','-T','--no-deps','--interactive','-i'].includes(a)))refuse('Docker Compose run options not allowed');
laneAuthFile(env,run);
const [base,override]=composeFiles(run);
return ['compose','-f',base,'-f',override,'run','--pull','never','--no-deps',...argv.slice(2)];
}
refuse('Docker mutation or unowned command');
}
export function cleanupProject(run,docker='docker'){
if(!/^mg197-[ab]-[a-f0-9]{12}$/.test(run.project))refuse('invalid Docker ownership project');
const invoke=typeof docker==='function'?docker:(args)=>execFileSync(docker,args,{encoding:'utf8',stdio:['ignore','pipe','pipe']});
const removed=[];
for(const [kind,list,filter] of [['container',['ps','-aq'],'com.docker.compose.project'],['network',['network','ls','-q'],'com.docker.compose.project']]){
const ids=invoke([...list,'--filter',`label=${filter}=${run.project}`]).trim().split(/\s+/).filter(Boolean);
for(const id of ids){if(!/^[a-f0-9]{12,64}$/.test(id))refuse('invalid Docker resource ID');
const label=invoke([kind,'inspect',id,'--format',`{{index .${kind==='container'?'Config.':''}Labels "${filter}"}}`]).trim();
if(label!==run.project)refuse('foreign Docker resource');
invoke(kind==='container'?['container','rm','-f',id]:['network','rm',id]);removed.push({kind,id});
}
const left=invoke([...list,'--filter',`label=${filter}=${run.project}`]).trim();if(left)refuse('Docker cleanup incomplete');
}
return {clean:true,removed};
}
if(process.argv[1]&&import.meta.url===pathToFileURL(process.argv[1]).href){
try{const run=JSON.parse(readFileSync(process.env.MOSAIC_GATE_DOCKER_RECORD,'utf8'));if(process.env.COMPOSE_PROJECT_NAME!==run.project)refuse('Compose project mismatch');
const argv=guardDocker(process.argv.slice(2),run,process.env);if(argv[0]==='compose')writeComposeOverride(run);const result=spawnSync(run.docker,argv,{stdio:'inherit'});process.exit(result.status??1);
}catch(e){process.stderr.write(`${e.message}\n`);process.exit(2);}
}