mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
fix(cso): replay expired snapshots from supplied source
Co-Authored-By: OpenAI Codex <noreply@openai.com>
This commit is contained in:
1 parent
b6ad9090d7
commit
476069d916
2 files changed
+9
-1
No files matched your search
+2
-1
@@ -465,7 +465,8 @@ async function replay(args:string[],dependencies:CsoCliDependencies){const repla
|
||||
if(bundle.requiredInputs.archives?.length&&!bundle.requiredInputs.dependencyClosures)throw new CsoError('MISSING_INPUT','Replay bundle predates retained dependency closures and cannot substitute current dependency state');
|
||||
let workDir=stored.dir,manifest:any,temporary:string|undefined;const retained=join(stored.dir,'snapshot');
|
||||
try{
|
||||
if(fs.existsSync(retained)){manifest=readJson(join(stored.dir,'snapshot.json'));assertSnapshot(stored.dir,manifest);}else if(source){const temp=newRun(source);temporary=temp.dir;workDir=temp.dir;manifest=await capture(source,workDir,undefined,undefined,{deadlineMs:replayDeadline});if(manifest.executionHash!==bundle.requiredInputs.sourceHash||manifest.originalHash!==bundle.requiredInputs.originalHash)throw new CsoError('INCOMPATIBLE_INPUT','Supplied source does not match the bundle input hashes');}else throw new CsoError('MISSING_INPUT','Retained source expired; supply explicitly matching source');
|
||||
const captureSupplied=async()=>{if(!source)throw new CsoError('MISSING_INPUT','Retained source expired; supply explicitly matching source');const temp=newRun(source);temporary=temp.dir;workDir=temp.dir;manifest=await capture(source,workDir,undefined,undefined,{deadlineMs:replayDeadline});if(manifest.executionHash!==bundle.requiredInputs.sourceHash||manifest.originalHash!==bundle.requiredInputs.originalHash)throw new CsoError('INCOMPATIBLE_INPUT','Supplied source does not match the bundle input hashes');};
|
||||
if(fs.existsSync(retained)){manifest=readJson(join(stored.dir,'snapshot.json'));const expiresAt=typeof manifest?.expiresAt==='string'?Date.parse(manifest.expiresAt):Number.NaN;if(!Number.isFinite(expiresAt)||new Date(expiresAt).toISOString()!==manifest.expiresAt)throw new CsoError('INCOMPATIBLE_INPUT','Retained snapshot expiry is invalid');if(expiresAt<=Date.now())await captureSupplied();else assertSnapshot(stored.dir,manifest);}else await captureSupplied();
|
||||
if(manifest.executionHash!==bundle.requiredInputs.sourceHash||manifest.originalHash!==bundle.requiredInputs.originalHash)throw new CsoError('INCOMPATIBLE_INPUT','Retained source hashes do not match the bundle');validateRepairBundle(bundle,id,join(workDir,'snapshot'),manifest);if(bundle.verification.policyHash!==ISOLATION_POLICY_HASH)throw new CsoError('INCOMPATIBLE_INPUT','Current helper isolation policy does not match the recorded bundle');const targetPlatform=bundle.requiredInputs.platform as 'linux/amd64'|'linux/arm64';let runtime;try{runtime=selectRuntime(bundle.verification.runtime.profile,targetPlatform,dependencies.runtimeCatalog);}catch(error:any){throw new CsoError('PREREQUISITE',error?.message||'Qualified replay runtime is unavailable');}const verifier=runtime;if(runtime.image!==bundle.requiredInputs.runtimeImage)throw new CsoError('INCOMPATIBLE_INPUT','Qualified runtime digest does not match the bundle');
|
||||
const endpoint=await dockerEndpoint(secureDirectory(join(workDir,'home'))),watchdogPath=dependencies.watchdogPath(),attemptDeadline=replayDeadline,delegate=new DockerVerificationExecutor(endpoint,watchdogPath,attemptDeadline);let executor:VerificationExecutor=delegate,archives:string[]=[],dependencyClosures:{before:DependencyClosure;after:DependencyClosure}|undefined,proofs:{before:PreparationProof;after:PreparationProof}|undefined;
|
||||
if(bundle.requiredInputs.dependencyClosures){if(!['node','bun','python','rails'].includes(runtime.stack))throw new CsoError('INCOMPATIBLE_INPUT','Replay dependency closure requires an application runtime');const replayClosures=bundle.requiredInputs.dependencyClosures as {before:DependencyClosure;after:DependencyClosure},plan=inspectPreparation(join(workDir,'snapshot'),runtime.stack as any);if(plan.status!=='ready')throw new CsoError('PREREQUISITE',plan.prerequisites.map((item:any)=>item.message).join('; ')||'Replay dependency metadata is not ready');const admission=admitPreparationRuntime({plan,platform:targetPlatform,profile:runtime.id,catalog:dependencies.runtimeCatalog}),runner=new DockerPreparationSandboxRunner({endpoint,watchdogPath,runRoot:workDir,controlRoot:secureDirectory(join(workDir,'preparation-execution')),admission}),preparation=new PreparationExecutor({cache:new PublicArchiveCache({root:publicArchiveCacheRoot(),stagingRoot:secureDirectory(join(workDir,'archive-staging'))}),runner,materializationRoot:secureDirectory(join(workDir,'archive-materializations'))}),beforeClosure=await preparation.acquire({plan,admission,snapshot:join(workDir,'snapshot'),deadline:attemptDeadline,offline:true,existingClosure:replayClosures.before});dependencyClosures={before:beforeClosure,after:replayClosures.after};archives=[...new Set([...beforeClosure.archives,...replayClosures.after.archives].map(archive=>archive.sha256))].sort();proofs={} as {before:PreparationProof;after:PreparationProof};executor=preparedVerificationExecutor({dir:workDir,findingId:bundle.request.findingId,runtimeProfile:runtime.id,stack:runtime.stack as any,targetPlatform,deadline:attemptDeadline,offline:true,runtimeCatalog:dependencies.runtimeCatalog,preparation,delegate,beforePlan:plan,beforeAdmission:admission,beforeClosure,closures:dependencyClosures,archiveHashes:archives,proofs,replay:replayClosures,persistClosures:false});}
|
||||
|
||||
@@ -69,6 +69,13 @@ describe('CSO interrupted-run and replay commands',()=>{
|
||||
|
||||
const leased=withLock(dir,()=>command(['replay',verified.bundle.id])) as ReturnType<typeof command>;expect(leased.status).not.toBe(0);expect(leased.stderr).toContain('INSUFFICIENT_CAPACITY');expect(leased.stderr).toContain('Another helper is updating this run');
|
||||
const retained=command(['replay',verified.bundle.id]);expect(retained.status).not.toBe(0);expect(retained.stderr).toContain('PREREQUISITE');expect(retained.stderr).toContain('MISSING_QUALIFIED_RUNTIME');
|
||||
const manifestPath=path.join(dir,'snapshot.json'),expiredManifest={...manifest,expiresAt:new Date(Date.now()-1_000).toISOString()};fs.writeFileSync(manifestPath,`${JSON.stringify(expiredManifest,null,2)}\n`);
|
||||
const expiredPresent=command(['replay',verified.bundle.id]);expect(expiredPresent.status).not.toBe(0);expect(expiredPresent.stderr).toContain('MISSING_INPUT');expect(expiredPresent.stderr).toContain('Retained source expired');
|
||||
const suppliedExpired=command(['replay',verified.bundle.id,'--source',repo]);expect(suppliedExpired.status).not.toBe(0);expect(suppliedExpired.stderr).toContain('PREREQUISITE');expect(suppliedExpired.stderr).toContain('MISSING_QUALIFIED_RUNTIME');expect(suppliedExpired.stderr).not.toContain('Retained source expired');
|
||||
const noncanonicalExpiry=new Date(Date.now()+86400_000).toISOString().replace(/\.\d{3}Z$/,'Z');fs.writeFileSync(manifestPath,`${JSON.stringify({...manifest,expiresAt:noncanonicalExpiry},null,2)}\n`);
|
||||
const invalidExpiry=command(['replay',verified.bundle.id,'--source',repo]);expect(invalidExpiry.status).not.toBe(0);expect(invalidExpiry.stderr).toContain('INCOMPATIBLE_INPUT');expect(invalidExpiry.stderr).toContain('Retained snapshot expiry is invalid');expect(invalidExpiry.stderr).not.toContain('MISSING_QUALIFIED_RUNTIME');
|
||||
fs.writeFileSync(manifestPath,`${JSON.stringify({...manifest,expiresAt:new Date(Date.now()+86400_000).toISOString()},null,2)}\n`);fs.writeFileSync(path.join(snapshot,'app.js'),'module.exports = "tampered"\n');
|
||||
const corruptRetained=command(['replay',verified.bundle.id,'--source',repo]);expect(corruptRetained.status).not.toBe(0);expect(corruptRetained.stderr).toContain('INCOMPATIBLE_INPUT');expect(corruptRetained.stderr).toContain('Retained snapshot changed');expect(corruptRetained.stderr).not.toContain('MISSING_QUALIFIED_RUNTIME');
|
||||
fs.rmSync(snapshot,{recursive:true,force:true});fs.rmSync(path.join(dir,'readable'),{recursive:true,force:true});
|
||||
const expired=command(['replay',verified.bundle.id]);expect(expired.status).not.toBe(0);expect(expired.stderr).toContain('MISSING_INPUT');expect(expired.stderr).toContain('Retained source expired');
|
||||
fs.writeFileSync(path.join(repo,'app.js'),'module.exports = "changed"\n');const changed=command(['replay',verified.bundle.id,'--source',repo]);expect(changed.status).not.toBe(0);expect(changed.stderr).toContain('INCOMPATIBLE_INPUT');expect(changed.stderr).toContain('does not match the bundle input hashes');
|
||||
|
||||
Reference in new issue
Block a user