Files
gstack/lib/cso/verification.ts
T
Garry TanandOpenAI Codex 4a3c6a8a3c v1.87.0.0 feat: add verified CSO audits and replayable repair bundles (#2852)
* feat(cso): add verified audits and replayable repair bundles

* fix(cso): harden qualification and setup boundaries

* fix(cso): assemble security canaries at runtime

* fix(cso): bound release proof and maintenance work

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): require complete evaluation reports

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): replay expired snapshots from supplied source

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* test(cso): synchronize DNS cancellation assertion

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore(ship): exempt repository owner from liveness proof

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* test(cso): make recheck retention overlap deterministic

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: bump version and changelog (v1.85.0.0)

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): pass native release gates

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: move release to v1.86.0.0

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): resolve rechecks by finding

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: move release to v1.87.0.0

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): pass macOS and Windows release gates

Normalize BSD wc output, compare Windows paths by filesystem identity, preserve portable snapshot race coverage, and narrow POSIX-only Windows fixtures.

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): harden native verification gates

* fix(cso): refine Windows native diagnostics

* test(cso): isolate Windows Git startup failure

* test(cso): stabilize Windows native diagnostics

* fix(cso): support hardened Git on Windows

* fix(cso): close final verification gaps

* test(cso): bound cold Docker fixture setup

* fix(cso): restore cross-platform free-suite gates

---------

Co-authored-by: OpenAI Codex <noreply@openai.com>
2026-09-14 15:14:58 -07:00

355 lines
71 KiB
TypeScript

import * as fs from 'node:fs';
import { randomBytes } from 'node:crypto';
import { spawn } from 'node:child_process';
import { basename, dirname, join } from 'node:path';
import {
AssertionWitnessBinding, AssertionWitnessReceipt, CsoError, PreparationProof, RepairBundle, RepairReviewArtifact, SnapshotManifest, VerificationManifest, VerificationObservation, VerificationRequest,
canonical, relativePath, sha256, snapshotPathHandleId, snapshotPathId, validateVerificationObservation, validateVerificationRequest,
} from './contracts';
import { QualifiedRuntime } from './runtime-catalog';
import { inspectPreparation, type CsoStack } from './preparation';
import { containedFile } from './snapshot';
import { DockerEndpoint, DockerGroup } from './docker';
import type { PreparedDatabaseContract } from './preparation-executor';
import { redact, sanitizeHelperForJson } from './process';
import { secureDirectory, writeJsonExclusive } from './state';
import { AssertionWitnessHandle, AssertionWitnessSession, WitnessedVerificationResult, assertionWitnessPairHash, testExecutionPassed, validateStoredAssertionWitnessReceipt, witnessObservationHash } from './witness';
export { testExecutionPassed } from './witness';
export interface VerificationExecutor {
observe(source:string,phase:'before'|'after',request:VerificationRequest,runtime:QualifiedRuntime,verifier:QualifiedRuntime,work:string,control:string,execution?:{environment:Record<string,string>;database?:PreparedDatabaseContract},testEvidence?:{minimumPassingTests:number[]},witness?:AssertionWitnessHandle):Promise<VerificationObservation|WitnessedVerificationResult>;
}
export interface FailedVerificationAttempt {
schemaVersion:3; artifactKind:'repair_candidate'; id:string; runId:string; findingId:string; createdAt:string; bundleIssued:false;
runtime:{image:string;platform:string;profile:string}; policyHash:string; requestHash:string; harnessHash:string; sourceHash:string;
request:VerificationRequest; patchHash:string; testToolchain:'runtime'|'project'; testCompletionAssurance:'self_reported'; preparationHash?:string;
before:VerificationObservation; after?:VerificationObservation; reproduction:'blocked'|'inconclusive'|'disproved'|'reproduced'; repair:'failed'|'proposed';
failure:{code:string;message:string};
}
export class VerificationAttemptError extends CsoError {
constructor(public causeError:CsoError,public attempt:FailedVerificationAttempt){super(causeError.code,`${causeError.message}; before-phase evidence retained as attempt ${attempt.id}`);this.name='VerificationAttemptError';}
}
async function attemptGuard(runDir:string,work:string,watchdog:string,deadline:number):Promise<()=>Promise<void>>{
const control=secureDirectory(join(runDir,'supervision',basename(work)));secureDirectory(work);const ready=join(control,'attempt.ready'),terminal=join(control,'attempt.terminal'),stopped=join(control,'attempt.stopped');
const child=spawn(watchdog,['--attempt-owner',String(process.pid),'--deadline',String(Math.ceil(deadline/1000)),'--control-dir',control,'--work-root',work,'--run-root',runDir],{cwd:control,env:{PATH:'/usr/bin:/bin'},detached:true,stdio:'ignore'});let failed=false;child.once('error',()=>{failed=true;});child.unref();for(let i=0;i<100&&!failed&&!fs.existsSync(ready);i++)await new Promise(resolve=>setTimeout(resolve,10));let alive=false;try{if(child.pid){process.kill(child.pid,0);alive=true;}}catch{}if(failed||!alive||!fs.existsSync(ready)){try{if(child.pid)process.kill(child.pid,'SIGKILL');}catch{}fs.rmSync(work,{recursive:true,force:true});throw new CsoError('ISOLATION_FAILED','Attempt execution-copy watchdog failed its startup handshake');}
return async()=>{fs.rmSync(work,{recursive:true,force:true});fs.writeFileSync(terminal,'normal cleanup complete\n',{mode:0o600,flag:'wx'});for(let i=0;i<100&&!fs.existsSync(stopped);i++)await new Promise(resolve=>setTimeout(resolve,10));if(!fs.existsSync(stopped))throw new CsoError('ISOLATION_FAILED','Attempt watchdog did not acknowledge execution-copy cleanup');};
}
function allFiles(root:string,at=root,ignore:((path:string)=>boolean)=()=>false):string[]{
const out:string[]=[];for(const entry of fs.readdirSync(at,{withFileTypes:true})){
const p=join(at,entry.name),relative=p.slice(root.length+1).replaceAll('\\','/');if(ignore(relative))continue;if(entry.isSymbolicLink()||(!entry.isDirectory()&&!entry.isFile()))throw new CsoError('UNSAFE_PATH','Execution copy contains a special file');
if(entry.isDirectory())out.push(...allFiles(root,p,ignore));else out.push(relative);
}return out.sort();
}
export function treeHash(root:string,predicate:((path:string)=>boolean)=()=>true):string{
return sha256(canonical(allFiles(root).filter(predicate).map(path=>{const file=containedFile(root,path),before=fs.lstatSync(file);if(!before.isFile()||before.isSymbolicLink()||before.nlink!==1)throw new CsoError('UNSAFE_PATH','Execution copy contains a special or hard-linked file');const body=fs.readFileSync(file),after=fs.lstatSync(file);if(before.ino!==after.ino||before.dev!==after.dev||before.size!==after.size||before.mtimeMs!==after.mtimeMs||before.ctimeMs!==after.ctimeMs)throw new CsoError('SNAPSHOT_RACE',`Execution copy changed while hashing: ${path}`);return[path,sha256(body),before.mode&0o777];})));
}
const DEPENDENCY=/(?:^|\/)(?:package(?:-lock)?\.json|npm-shrinkwrap\.json|bun\.lock|uv\.lock|requirements[^/]*\.txt|pyproject\.toml|setup\.(?:py|cfg)|Gemfile(?:\.lock)?|[^/]+\.gemspec)$/;
const CONFIG=/(?:^|\/)(?:config\/.+|\.env|Dockerfile|Procfile|.*\.(?:toml|ya?ml|json))$/;
export function fileEffect(path:string):'source'|'configuration'|'dependency'{return DEPENDENCY.test(path)?'dependency':CONFIG.test(path)?'configuration':'source';}
export function patchHash(request:Pick<VerificationRequest,'changes'>):string{return sha256(canonical(request.changes));}
export function resolveVerificationRequestPaths(manifest:SnapshotManifest,request:VerificationRequest):VerificationRequest{
const resolve=(reference:string):string=>{const id=snapshotPathHandleId(reference);if(!id)return relativePath(reference);const entry=manifest.entries.find(item=>item.pathId===id);if(entry)return entry.path;const deleted=manifest.deletedPaths?.find(item=>item.pathId===id);if(deleted)return deleted.path;const changed=manifest.changedPaths?.find(path=>snapshotPathId(manifest.root,path)===id);if(changed)return changed;throw new CsoError('INVALID_SCHEMA',`Verification path handle is outside the retained snapshot: ${reference}`);};
const argument=(value:string):string=>{const direct=snapshotPathHandleId(value);if(direct)return resolve(value);if(value.startsWith('./')&&snapshotPathHandleId(value.slice(2)))return `./${resolve(value.slice(2))}`;return value;};
const command=(value:VerificationRequest['start']):VerificationRequest['start']=>({...value,args:value.args.map(argument)});
return{...request,start:command(request.start),existingTests:request.existingTests.map(command),boundaryFiles:request.boundaryFiles.map(resolve),testFiles:request.testFiles.map(resolve),changes:request.changes.map(change=>({...change,path:resolve(change.path)}))};
}
export function reviewRequestHash(request:VerificationRequest):string{const {artifactId,...review}=request.review;return sha256(canonical({...request,review}));}
export function reviewArtifactIdentity(artifact:RepairReviewArtifact):string{const {id,...bound}=artifact;return sha256(canonical(bound)).slice(0,32);}
export function makeReviewArtifact(runId:string,request:VerificationRequest,producer:string):RepairReviewArtifact{
if(!producer||producer.length>200||producer===request.review.reviewer)throw new CsoError('INVALID_SCHEMA','Repair producer and independent reviewer identities must be distinct');
if(!request.review.independent)throw new CsoError('INVALID_SCHEMA','Repair review must be explicitly independent');
const artifact:RepairReviewArtifact={schemaVersion:3,id:'',runId,findingId:request.findingId,createdAt:new Date().toISOString(),producer,reviewer:request.review.reviewer,assurance:'self_attested',requestHash:reviewRequestHash(request),patchHash:patchHash(request),rootCauseRepaired:request.review.rootCauseRepaired,featurePreserved:request.review.featurePreserved,boundaryMocks:request.review.boundaryMocks,rationale:request.review.rationale};artifact.id=reviewArtifactIdentity(artifact);return artifact;
}
export function validateReviewArtifact(value:unknown,runId:string,request:VerificationRequest):RepairReviewArtifact{
const artifact=value as RepairReviewArtifact;if(!artifact||artifact.schemaVersion!==3||artifact.assurance!=='self_attested'||artifact.runId!==runId||artifact.findingId!==request.findingId||artifact.id!==request.review.artifactId||reviewArtifactIdentity(artifact)!==artifact.id||artifact.requestHash!==reviewRequestHash(request)||artifact.patchHash!==patchHash(request)||artifact.reviewer!==request.review.reviewer||artifact.producer===artifact.reviewer||artifact.rootCauseRepaired!==request.review.rootCauseRepaired||artifact.featurePreserved!==request.review.featurePreserved||artifact.boundaryMocks!==request.review.boundaryMocks||artifact.rationale!==request.review.rationale)throw new CsoError('INCOMPATIBLE_INPUT','Self-attested repair-review artifact does not bind this request');return artifact;
}
export function verificationIdentity(manifest:VerificationManifest):string{const {id,...bound}=manifest;return sha256(canonical(bound)).slice(0,32);}
export function verificationHarnessHash(request:VerificationRequest,sourceRoot:string):string{
const testInputs=request.testFiles.map(path=>{const file=containedFile(sourceRoot,path);if(!fs.existsSync(file))throw new CsoError('INCOMPATIBLE_INPUT',`Immutable existing-test input is missing: ${path}`);const stat=fs.lstatSync(file);if(!stat.isFile()||stat.isSymbolicLink()||stat.nlink!==1)throw new CsoError('UNSAFE_PATH',`Immutable existing-test input is unsafe: ${path}`);return[path,sha256(fs.readFileSync(file)),stat.mode&0o777];});
return sha256(canonical({port:request.port,start:request.start,legitimate:request.legitimate,security:request.security,existingTests:request.existingTests,testInputs}));
}
export interface CanonicalTestPlan { commands:VerificationRequest['existingTests'];files:string[];kind:string;toolchain:'runtime'|'project';minimumPassingTests:number[];signature:string }
export interface CanonicalStartPlan { command:VerificationRequest['start'];kind:string;signature:string;entrypointFiles:string[] }
const TEST_TREE=/(?:^|\/)(?:test|tests|__tests__|spec|fixtures|__fixtures__|testdata)(?:\/|$)/;
const NODE_TEST_CONFIG=/(?:^|\/)(?:(?:jest|vitest|vite|playwright|cypress|karma|babel|ava|webpack)\.(?:config|conf)\.[^/]+|(?:jest|vitest|playwright|cypress|babel|ava|webpack)\.config\.[^/]+|\.mocharc(?:\.[^/]+)?|\.babelrc(?:\.[^/]+)?|tsconfig(?:\.[^/]+)?\.json|bunfig\.toml)$/;
const BUN_RUNTIME_POLICY_ARGS=['--no-install','--config=/opt/cso/no-auto-install.toml'];
// -S prevents dependency-provided .pth startup code from running before the
// trusted bootstrap. Add the venv's fixed Linux purelib directory directly,
// without processing .pth files, and import each runner before application cwd.
const PYTHON_PURELIB="os.path.join(os.path.dirname(os.path.dirname(sys.executable)),'lib',f'python{sys.version_info.major}.{sys.version_info.minor}','site-packages')";
const PYTEST_BOOTSTRAP=`import os,sys;sys.path.append(${PYTHON_PURELIB});import pytest;sys.path.insert(0,os.getcwd());raise SystemExit(pytest.main(sys.argv[1:]))`;
const UNITTEST_BOOTSTRAP=`import os,sys,unittest;sys.path.append(${PYTHON_PURELIB});sys.path.insert(0,os.getcwd());unittest.main(module=None,argv=['unittest',*sys.argv[1:]])`;
const DJANGO_BOOTSTRAP=`import os,sys,runpy;sys.path.append(${PYTHON_PURELIB});import django;sys.path.insert(0,os.getcwd());sys.argv=['manage.py',*sys.argv[1:]];runpy.run_path('manage.py',run_name='__main__')`;
const FLASK_BOOTSTRAP=`import os,sys;sys.path.append(${PYTHON_PURELIB});from flask.cli import main as _cso_main;sys.path.insert(0,os.getcwd());sys.argv=['flask',*sys.argv[1:]];_cso_main()`;
const UVICORN_BOOTSTRAP=`import os,sys;sys.path.append(${PYTHON_PURELIB});from uvicorn.main import main as _cso_main;sys.path.insert(0,os.getcwd());sys.argv=['uvicorn',*sys.argv[1:]];_cso_main()`;
function fileText(root:string,path:string):string{try{return fs.readFileSync(containedFile(root,path),'utf8');}catch{throw new CsoError('MISSING_INPUT',`Canonical test input is missing or unreadable: ${path}`);}}
function packageTestProjection(root:string,paths:string[]):unknown[]{return paths.filter(path=>/(?:^|\/)package\.json$/.test(path)&&!TEST_TREE.test(path)).map(path=>{let value:Record<string,any>;try{value=JSON.parse(fileText(root,path));}catch{throw new CsoError('MISSING_INPUT',`Canonical package test configuration is invalid: ${path}`);}if(!value||typeof value!=='object'||Array.isArray(value))throw new CsoError('MISSING_INPUT',`Canonical package test configuration is invalid: ${path}`);return{path,type:value.type??null,workspaces:value.workspaces??null,scripts:value.scripts??null,jest:value.jest??null,vitest:value.vitest??null,mocha:value.mocha??null,ava:value.ava??null,nyc:value.nyc??null};});}
function withoutJsCommentsAndStrings(value:string):string{return value.replace(/\/\*[\s\S]*?\*\/|\/\/[^\r\n]*|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`/g,match=>' '.repeat(match.length));}
function assertJavascriptTestRegistrations(root:string,tests:string[]):number{
const code=tests.map(path=>withoutJsCommentsAndStrings(fileText(root,path))).join('\n');
if(/\b(?:fdescribe|fit)\s*\(|\b(?:describe|test|it)\s*\.\s*(?:only|concurrent\s*\.\s*only)\b/.test(code))throw new CsoError('MISSING_INPUT','Canonical JavaScript tests cannot certify a focused-only suite');
const registrations=[...code.matchAll(/\b(?:test|it)\s*(?:\.\s*(?:concurrent|each)\s*(?:\([^)]*\))?)?\s*\(/g)].length;if(!registrations)throw new CsoError('MISSING_INPUT','Canonical JavaScript tests need static evidence of at least one non-skipped test or it registration');return registrations;
}
function boundedTestPaths(tests:string[]):string[]{
if(!tests.length)throw new CsoError('MISSING_INPUT','No canonical project test sources were found');
if(tests.length>1000)throw new CsoError('MISSING_INPUT','Canonical project test suite exceeds the 1,000-file verification limit');
const paths=tests.map(path=>`./${path}`);if(paths.reduce((bytes,path)=>bytes+Buffer.byteLength(path)+1,0)>128*1024)throw new CsoError('MISSING_INPUT','Canonical project test paths exceed the bounded direct-runner argument limit');return paths;
}
function directPackageTest(root:string,stack:'node'|'bun',manifest:Record<string,any>,tests:string[],configuration:string[]):{command:VerificationRequest['existingTests'][number];kind:string;runner:string;toolchain:'runtime'|'project';minimumPassingTests:number}{
const scripts=manifest.scripts;if(!scripts||typeof scripts!=='object'||Array.isArray(scripts))throw new CsoError('MISSING_INPUT',`Canonical ${stack} package test scripts are missing or invalid`);
const script=scripts.test;if(typeof script!=='string'||!script.trim()||script.length>4096||/no test specified|^\s*(?:true|:|exit\s+0)\s*$/i.test(script))throw new CsoError('MISSING_INPUT',`Canonical ${stack} test script is missing or a placeholder`);
for(const hook of ['pretest','posttest']){
const value=scripts[hook];if(value!==undefined&&typeof value!=='string')throw new CsoError('MISSING_INPUT',`Canonical ${stack} ${hook} lifecycle hook is invalid`);
if(typeof value==='string'&&value.trim())throw new CsoError('MISSING_INPUT',`Canonical ${stack} tests cannot certify through package lifecycle hooks; remove ${hook} or run the direct standard runner`);
}
if(/[;&|><`$()\\\r\n]/.test(script))throw new CsoError('MISSING_INPUT',`Canonical ${stack} tests require one recognized direct standard runner; local wrappers and shell composition are not admitted`);
const words=script.trim().split(/\s+/),standard=['jest','vitest','mocha','ava'],paths=boundedTestPaths(tests),minimumPassingTests=assertJavascriptTestRegistrations(root,tests);
if(canonical(words)===canonical(['node','--test'])){
return{command:{executable:'/usr/local/bin/node',args:['--test','--test-reporter=tap',...paths]},kind:'direct node --test with TAP count evidence',runner:'node',toolchain:'runtime',minimumPassingTests};
}
if(canonical(words)===canonical(['bun','test'])){
if(stack!=='bun')throw new CsoError('MISSING_INPUT','Canonical Node verification cannot depend on the Bun test runtime');
return{command:{executable:'/usr/local/bin/bun',args:[...BUN_RUNTIME_POLICY_ARGS,'test',...paths]},kind:'direct bun test with automatic installation disabled',runner:'bun',toolchain:'runtime',minimumPassingTests};
}
const runner=words.length===1&&standard.includes(words[0])?words[0]:words.length===3&&words[0]==='npx'&&words[1]==='--no-install'&&standard.includes(words[2])?words[2]:undefined;
if(!runner)throw new CsoError('MISSING_INPUT',`Canonical ${stack} tests require one recognized direct standard runner; local wrappers and shell composition are not admitted`);
const control=canonical({embedded:manifest[runner]??null,files:configuration.filter(path=>NODE_TEST_CONFIG.test(path)).map(path=>[path,fileText(root,path)])});
if(/(?:collectOnly|dryRun|passWithNoTests|testNamePattern|\b(?:grep|fgrep|match)\b|--(?:collect-only|dry-run|grep|fgrep|match|passWithNoTests))/i.test(control))throw new CsoError('MISSING_INPUT',`Canonical ${runner} configuration cannot focus, skip execution, or allow an empty suite`);
const args=runner==='jest'?['--runTestsByPath','--passWithNoTests=false','--json',...paths]:runner==='vitest'?['run','--passWithNoTests=false','--reporter=verbose',...paths]:runner==='mocha'?['--fail-zero','--no-dry-run','--forbid-only','--reporter','json',...paths]:['--tap',...paths];
return{command:{executable:`/work/node_modules/.bin/${runner}`,args},kind:`direct local ${runner}`,runner,toolchain:'project',minimumPassingTests};
}
function directPackageStartEntrypoint(root:string,stack:'node'|'bun',script:string):string{
if(/[;&|><`$()\\\r\n]/.test(script))throw new CsoError('MISSING_INPUT',`Canonical ${stack} startup cannot use shell composition`);
const words=script.trim().split(/\s+/),runner=words.shift();
if(runner!==stack||words.length!==1||!/^[A-Za-z0-9_./-]+\.(?:[cm]?[jt]s|jsx|tsx)$/.test(words[0])||words[0].startsWith('/')||words[0].split('/').includes('..'))throw new CsoError('MISSING_INPUT',`Canonical ${stack} package startup requires one direct contained ${stack} entrypoint`);
const path=relativePath(words[0]);if(!executableSource(root,path))throw new CsoError('MISSING_INPUT',`Canonical ${stack} package startup entrypoint is missing or unsafe: ${path}`);return path;
}
function pyprojectTestProjection(root:string,path:string):unknown{try{const value=Bun.TOML.parse(fileText(root,path)) as Record<string,any>;return{tool:{pytest:value?.tool?.pytest??null,coverage:value?.tool?.coverage??null},projectScripts:value?.project?.scripts??null};}catch{throw new CsoError('MISSING_INPUT','Canonical Python test configuration is invalid: pyproject.toml');}}
export function canonicalTestPlan(sourceRoot:string,stack:CsoStack):CanonicalTestPlan{
const generated=(path:string)=>{const first=path.split('/')[0];return stack==='node'?first==='node_modules'||first==='.cso-npm-cache':stack==='bun'?first==='node_modules'||first==='.cso-bun-cache':stack==='python'?first==='.venv'||first==='.cso-uv-cache'||path==='.gstack-cso-public-requirements.txt':path.startsWith('vendor/bundle/')||first==='.cso-bundle'||first==='.cso-gems';};
const all=allFiles(sourceRoot,sourceRoot,generated);let tests:string[]=[],configuration:string[]=[],commands:VerificationRequest['existingTests']=[],kind='',toolchain:'runtime'|'project'='runtime',minimumPassingTests:number[]=[],runnerEvidence:unknown={};
if(stack==='node'||stack==='bun'){
let manifest:Record<string,any>;try{manifest=JSON.parse(fileText(sourceRoot,'package.json'));}catch{throw new CsoError('MISSING_INPUT',`Canonical ${stack} package.json is missing or invalid`);}
tests=all.filter(path=>/(?:^|\/)(?:test|tests|__tests__)\/.*\.(?:[cm]?js|tsx?|jsx)$|\.(?:test|spec)\.(?:[cm]?js|tsx?|jsx)$/.test(path));configuration=all.filter(path=>TEST_TREE.test(path)||NODE_TEST_CONFIG.test(path));const direct=directPackageTest(sourceRoot,stack,manifest,tests,configuration);commands=[direct.command];kind=direct.kind;toolchain=direct.toolchain;minimumPassingTests=[direct.minimumPassingTests];runnerEvidence={runner:direct.runner,declaredScript:manifest.scripts.test,packages:packageTestProjection(sourceRoot,all),minimumPassingTests:direct.minimumPassingTests};
}else if(stack==='python'){
tests=all.filter(path=>/(?:^|\/)(?:test|tests)\/.*\.py$|(?:^|\/)test_[^/]+\.py$|_test\.py$/.test(path));configuration=all.filter(path=>TEST_TREE.test(path)||/(?:^|\/)(?:conftest\.py|\.?pytest\.ini|\.?pytest\.toml|setup\.cfg|tox\.ini|noxfile\.py)$/.test(path));const bodies=tests.map(path=>fileText(sourceRoot,path)),configBodies=configuration.map(path=>fileText(sourceRoot,path)),pyproject=all.includes('pyproject.toml')?pyprojectTestProjection(sourceRoot,'pyproject.toml'):null;const pytestEvidence=all.some(path=>/(?:^|\/)(?:conftest\.py|\.?pytest\.ini|\.?pytest\.toml)$/.test(path))||[...bodies,...configBodies].some(body=>/(?:^|\n)\s*(?:import pytest|from pytest\b|@pytest\.)/m.test(body)||/(?:^|\n)(?:async\s+)?def test_[A-Za-z0-9_]*\s*\(/m.test(body));const unittestEvidence=bodies.some(body=>/(?:^|\n)\s*(?:import unittest|from unittest\b)|unittest\.TestCase|TestCase\s*\)/m.test(body));if(!pytestEvidence&&!unittestEvidence)throw new CsoError('MISSING_INPUT','Python test runner is ambiguous; declare pytest evidence or a unittest suite');const usePytest=pytestEvidence,paths=boundedTestPaths(tests),pytestControl=[...configBodies,canonical(pyproject)].join('\n');if(usePytest&&/(?:--collect-only|\s--co\b|--setup-(?:only|plan)|--fixtures(?:-per-test)?|--no-summary|\baddopts[^\n]*(?:\s-k\b|\s-m\b|--ignore\b|--deselect\b|(?:^|\s)-q{2,}\b))/i.test(pytestControl))throw new CsoError('MISSING_INPUT','Canonical pytest configuration cannot collect only, focus, deselect, suppress its count, or skip test execution');commands=[{executable:'/work/.venv/bin/python',args:usePytest?['-I','-S','-c',PYTEST_BOOTSTRAP,'-q','--color=no','--',...paths]:['-I','-S','-c',UNITTEST_BOOTSTRAP,...paths]}];kind=usePytest?'isolated prepared pytest with explicit files, positive summary, and no .pth startup':'isolated standard-library unittest with explicit files and no .pth startup';toolchain=usePytest?'project':'runtime';minimumPassingTests=[1];runnerEvidence={runner:usePytest?'pytest':'unittest',bootstrap:usePytest?PYTEST_BOOTSTRAP:UNITTEST_BOOTSTRAP,siteInitialization:false,reporter:usePytest?'quiet-positive-summary-no-color':'unittest-summary',pyproject};
}else{
const specs=all.filter(path=>/(?:^|\/)spec\/.*_spec\.rb$/.test(path)),rails=all.filter(path=>/(?:^|\/)test\/.*_test\.rb$/.test(path));tests=[...specs,...rails];configuration=all.filter(path=>TEST_TREE.test(path)||/(?:^|\/)\.rspec(?:-local)?$/.test(path));const rspecControl=configuration.filter(path=>/(?:^|\/)\.rspec(?:-local)?$/.test(path)).map(path=>fileText(sourceRoot,path)).join('\n');if(/--(?:dry-run|tag|example|pattern|exclude-pattern|only-failures|next-failure)\b/.test(rspecControl))throw new CsoError('MISSING_INPUT','Canonical RSpec configuration cannot dry-run, focus, filter, or select only prior failures');commands=[...(specs.length?[{executable:'/usr/local/bin/bundle',args:['exec','rspec','--format','json','--',...boundedTestPaths(specs)]}]:[]),...(rails.length?[{executable:'/usr/local/bin/bundle',args:['exec','rails','test','--no-color',...boundedTestPaths(rails)]}]:[])];kind=commands.map(command=>command.args.join(' ')).join(' + ');toolchain='project';minimumPassingTests=commands.map(()=>1);runnerEvidence={rspec:specs.length>0,minitest:rails.length>0,reporters:specs.length?['rspec-json',...(rails.length?['rails-summary-no-color']:[])]:['rails-summary-no-color']};
}
tests=[...new Set(tests)].sort();configuration=[...new Set(configuration)].sort();if(!tests.length)throw new CsoError('MISSING_INPUT',`No canonical ${stack} project test sources were found`);const selected=[...new Set([...configuration,...tests])].sort();if(selected.length>1000)throw new CsoError('MISSING_INPUT','Canonical project test suite exceeds the 1,000-file verification limit');if(minimumPassingTests.length!==commands.length||minimumPassingTests.some(value=>!Number.isInteger(value)||value<1))throw new CsoError('MISSING_INPUT','Canonical test plan could not derive a positive execution-count floor');return{commands,files:selected,kind,toolchain,minimumPassingTests,signature:sha256(canonical({stack,runnerEvidence,commands,files:selected,toolchain,minimumPassingTests}))};
}
export function assertCanonicalTestPlan(request:VerificationRequest,sourceRoot:string,stack:CsoStack):CanonicalTestPlan{const plan=canonicalTestPlan(sourceRoot,stack);if(canonical(request.existingTests)!==canonical(plan.commands)||canonical([...request.testFiles].sort())!==canonical(plan.files))throw new CsoError('INVALID_SCHEMA',`Existing tests must use the helper-derived full ${plan.kind} suite and its immutable inputs`);return plan;}
function executableSource(root:string,path:string):boolean{try{const file=containedFile(root,relativePath(path)),stat=fs.lstatSync(file);return stat.isFile()&&!stat.isSymbolicLink()&&stat.nlink===1;}catch{return false;}}
function rejectPythonFrameworkShadows(root:string,framework:string,names:string[]):void{
for(const name of names)for(const candidate of [`${name}.py`,name]){
const file=containedFile(root,candidate);if(fs.existsSync(file))throw new CsoError('PREREQUISITE',`Canonical ${framework} startup rejects root import shadow: ${candidate}`);
}
}
export function canonicalStartPlan(sourceRoot:string,stack:CsoStack,port:number):CanonicalStartPlan{
if(!Number.isInteger(port)||port<1024||port>65535)throw new CsoError('INVALID_SCHEMA','Canonical application start needs a loopback port from 1024 to 65535');
let command:VerificationRequest['start'],kind:string,entrypointFiles:string[]=[],evidence:unknown;
if(stack==='node'||stack==='bun'){
let manifest:Record<string,any>;try{manifest=JSON.parse(fileText(sourceRoot,'package.json'));}catch{throw new CsoError('MISSING_INPUT',`Canonical ${stack} package.json is missing or invalid`);}const start=manifest?.scripts?.start;
if(typeof start==='string'&&start.trim()&&!/no start|^\s*(?:true|:|exit\s+0)\s*$/i.test(start)){for(const hook of ['prestart','poststart']){const value=manifest?.scripts?.[hook];if(value!==undefined&&typeof value!=='string')throw new CsoError('MISSING_INPUT',`Canonical ${stack} ${hook} lifecycle hook is invalid`);if(typeof value==='string'&&value.trim())throw new CsoError('MISSING_INPUT',`Canonical ${stack} startup cannot certify through package lifecycle hooks; remove ${hook} or run the direct entrypoint`);}const entry=directPackageStartEntrypoint(sourceRoot,stack,start);command={executable:stack==='node'?'/usr/local/bin/node':'/usr/local/bin/bun',args:stack==='node'?[entry]:[...BUN_RUNTIME_POLICY_ARGS,entry]};kind=`direct ${stack} package start`;evidence={script:start,entry};entrypointFiles=['package.json',entry];}
else{const declared=typeof manifest?.main==='string'&&manifest.main.length<4096?manifest.main:undefined,candidates=[...(declared?[declared]:[]),...'server.js,app.js,index.js,server.mjs,app.mjs,index.mjs'.split(',')].filter((value,index,all)=>all.indexOf(value)===index&&executableSource(sourceRoot,value));if(candidates.length!==1)throw new CsoError('MISSING_INPUT',`Canonical ${stack} startup is ambiguous; declare one non-placeholder start script or one conventional main entrypoint`);const entry=relativePath(candidates[0]);command={executable:stack==='node'?'/usr/local/bin/node':'/usr/local/bin/bun',args:stack==='node'?[entry]:[...BUN_RUNTIME_POLICY_ARGS,entry]};kind=`${stack} ${entry}`;evidence={entry};entrypointFiles=[entry];}
}else if(stack==='rails'){
entrypointFiles=['config/application.rb','config/environment.rb'].filter(path=>executableSource(sourceRoot,path));if(entrypointFiles.length!==2)throw new CsoError('MISSING_INPUT','Canonical Rails startup requires config/application.rb and config/environment.rb');command={executable:'/usr/local/bin/bundle',args:['exec','rails','server','-b','127.0.0.1','-p',String(port)]};kind='Rails loopback server';evidence={entrypointFiles};
}else{
const preparation=inspectPreparation(sourceRoot,'python');if(preparation.status!=='ready')throw new CsoError('PREREQUISITE',preparation.prerequisites.map(item=>item.message).join('; ')||'Python dependency metadata is incomplete');const dependencies=new Set(preparation.inputs.map(input=>input.name.toLowerCase().replaceAll('_','-'))),choices:Array<{kind:string;command:VerificationRequest['start'];files:string[];evidence:unknown}>=[];
if(dependencies.has('django')&&executableSource(sourceRoot,'manage.py')){rejectPythonFrameworkShadows(sourceRoot,'Django',['django']);choices.push({kind:'isolated Django loopback server without .pth startup',command:{executable:'/work/.venv/bin/python',args:['-I','-S','-c',DJANGO_BOOTSTRAP,'runserver',`127.0.0.1:${port}`,'--noreload']},files:['manage.py'],evidence:{framework:'django',bootstrap:DJANGO_BOOTSTRAP,siteInitialization:false,rootImportShadowsRejected:['django.py','django/']}});}
for(const file of ['app.py','application.py','wsgi.py'])if(dependencies.has('flask')&&executableSource(sourceRoot,file)){const body=fileText(sourceRoot,file),match=body.match(/(?:^|\n)\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*Flask\s*\(/m);if(match){rejectPythonFrameworkShadows(sourceRoot,'Flask',['flask']);choices.push({kind:'isolated Flask loopback server without .pth startup',command:{executable:'/work/.venv/bin/python',args:['-I','-S','-c',FLASK_BOOTSTRAP,'--app',`${file.replace(/\.py$/,'')}:${match[1]}`,'run','--host','127.0.0.1','--port',String(port)]},files:[file],evidence:{framework:'flask',module:file,symbol:match[1],bootstrap:FLASK_BOOTSTRAP,siteInitialization:false,rootImportShadowsRejected:['flask.py','flask/']}});}}
for(const file of ['main.py','app.py','server.py'])if(dependencies.has('fastapi')&&dependencies.has('uvicorn')&&executableSource(sourceRoot,file)){const body=fileText(sourceRoot,file),match=body.match(/(?:^|\n)\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*FastAPI\s*\(/m);if(match){rejectPythonFrameworkShadows(sourceRoot,'FastAPI/Uvicorn',['fastapi','uvicorn']);choices.push({kind:'isolated FastAPI loopback server without .pth startup',command:{executable:'/work/.venv/bin/python',args:['-I','-S','-c',UVICORN_BOOTSTRAP,`${file.replace(/\.py$/,'')}:${match[1]}`,'--app-dir','/work','--host','127.0.0.1','--port',String(port)]},files:[file],evidence:{framework:'fastapi',module:file,symbol:match[1],bootstrap:UVICORN_BOOTSTRAP,siteInitialization:false,rootImportShadowsRejected:['fastapi.py','fastapi/','uvicorn.py','uvicorn/']}});}}
if(preparation.inputs.length===0){const direct=['app.py','application.py','server.py','main.py'].filter(file=>executableSource(sourceRoot,file));if(direct.length===1){const entry=direct[0];choices.push({kind:'isolated standard-library Python application',command:{executable:'/usr/local/bin/python',args:['-I',entry]},files:[entry],evidence:{framework:'standard-library',entry,isolatedMode:true,dependencyClosure:'empty'}});}}
const unique=choices.filter((choice,index)=>choices.findIndex(other=>canonical(other.command)===canonical(choice.command))===index);if(unique.length!==1)throw new CsoError('MISSING_INPUT','Canonical Python startup is unavailable or ambiguous; use one supported Django, Flask, or FastAPI entrypoint with locked runtime dependencies');({command,kind,evidence}=unique[0]);entrypointFiles=unique[0].files;
}
const entrypointEvidence=entrypointFiles.map(path=>{const file=containedFile(sourceRoot,path),stat=fs.lstatSync(file);if(!stat.isFile()||stat.isSymbolicLink()||stat.nlink!==1)throw new CsoError('UNSAFE_PATH',`Canonical startup input is unsafe: ${path}`);return{path,sha256:sha256(fs.readFileSync(file)),mode:stat.mode&0o777};});
return{command,kind,entrypointFiles,signature:sha256(canonical({stack,kind,evidence,command,entrypointEvidence}))};
}
export function assertCanonicalStartPlan(request:VerificationRequest,sourceRoot:string,stack:CsoStack):CanonicalStartPlan{const plan=canonicalStartPlan(sourceRoot,stack,request.port);if(canonical(request.start)!==canonical(plan.command))throw new CsoError('INVALID_SCHEMA',`Application start must use the helper-derived ${plan.kind} command`);return plan;}
export function preparePatchedSource(snapshot:string,target:string,request:VerificationRequest):void{
secureDirectory(target);fs.cpSync(snapshot,target,{recursive:true,errorOnExist:false,force:true,preserveTimestamps:false});
for(const change of request.changes){
const path=relativePath(change.path),file=containedFile(target,path),exists=fs.existsSync(file);
if(change.beforeSha256===null&&exists)throw new CsoError('INCOMPATIBLE_INPUT',`Expected new patch path already exists: ${path}`);
if(change.beforeSha256!==null&&(!exists||sha256(fs.readFileSync(file))!==change.beforeSha256))throw new CsoError('INCOMPATIBLE_INPUT',`Patch preimage does not match: ${path}`);
const derived=fileEffect(path);if(change.effect!==derived)throw new CsoError('INVALID_SCHEMA',`${path} must be declared as ${derived}, not ${change.effect}`);
if(change.after===null){fs.unlinkSync(file);continue;}
const safe=redact(change.after);if(safe!==change.after)throw new CsoError('REDACTION_FAILED',`Patch content for ${path} contains material that cannot enter a repair bundle`);
const mode=exists?(fs.statSync(file).mode&0o777):0o600;secureDirectory(dirname(file));fs.writeFileSync(file,change.after,{mode});
}
}
export function certify(params:{runId:string;manifest:SnapshotManifest;request:VerificationRequest;identityRequest?:VerificationRequest;runtime:QualifiedRuntime;verifier:QualifiedRuntime;before:VerificationObservation;after:VerificationObservation;beforeRoot:string;afterRoot:string;policyHash:string;auditPolicyHash?:string;archives:string[];dependencyClosures?:{before:unknown;after:unknown};preparation?:{before:PreparationProof;after:PreparationProof};reviewArtifact?:RepairReviewArtifact;startPlanHash?:string;testPlanHash?:string;testToolchain:'runtime'|'project';minimumPassingTests?:number[];witness?:{before:AssertionWitnessReceipt;after:AssertionWitnessReceipt}}):{manifest:VerificationManifest;bundle?:RepairBundle}{
const {request}=params,identityRequest=params.identityRequest??request,pHash=patchHash(identityRequest),harnessHash=verificationHarnessHash(request,params.beforeRoot),afterHarnessHash=verificationHarnessHash(request,params.afterRoot),fixturesHash=sha256(canonical(identityRequest.fixtures));if(afterHarnessHash!==harnessHash)throw new CsoError('ASSERTION_FAILED','Repair changed immutable existing-test inputs');
if(!['runtime','project'].includes(params.testToolchain))throw new CsoError('INVALID_SCHEMA','Verification test toolchain must be helper-derived as runtime or project');
if(identityRequest.review.reviewedPatchHash!==pHash)throw new CsoError('INVALID_SCHEMA',`Independent review binds the wrong patch hash; expected ${pHash}`);
const sourceAfter=treeHash(params.afterRoot),dependenciesBefore=treeHash(params.beforeRoot,p=>DEPENDENCY.test(p)),dependenciesAfter=treeHash(params.afterRoot,p=>DEPENDENCY.test(p)),configurationBefore=treeHash(params.beforeRoot,p=>CONFIG.test(p)&&!DEPENDENCY.test(p)),configurationAfter=treeHash(params.afterRoot,p=>CONFIG.test(p)&&!DEPENDENCY.test(p));
const mechanical=params.before.booted&&params.before.legitimate&&params.before.security==='intended_failure'&&params.before.existingTests&&params.after.booted&&params.after.legitimate&&params.after.security==='pass'&&params.after.existingTests;
const reviewGate=identityRequest.review.independent&&identityRequest.review.rootCauseRepaired&&identityRequest.review.featurePreserved&&!identityRequest.review.boundaryMocks;
// Application code shares the project test process and can forge reporter
// output or terminate the runner. The signed receipt authenticates the
// separate verifier assertions; project-test completion stays self-reported.
const testCompletionAssurance='self_reported' as const;
const reviewAssurance=params.reviewArtifact?.assurance??'self_attested';
const inconclusive=!params.before.booted||!params.before.legitimate||params.before.security==='inconclusive'||!params.after.booted||params.after.security==='inconclusive';
if(params.preparation&&(canonical(params.preparation.before.transformations)!==canonical(params.preparation.after.transformations)||
params.preparation.before.databaseHash!==params.preparation.after.databaseHash))throw new CsoError('ASSERTION_FAILED','Repair changed the synthetic preparation or database boundary');
if(mechanical&&reviewGate&&params.testToolchain==='project'){
if(request.changes.some(change=>change.effect==='dependency'))throw new CsoError('PREREQUISITE','Runtime-tested dependency repairs require a test runner pinned in the qualified runtime; project-installed test toolchains may change with the repair');
if(!params.preparation)throw new CsoError('ASSERTION_FAILED','Project-installed test toolchains require before/after prepared dependency proofs');
if(params.preparation.before.preparedDependencyHash!==params.preparation.after.preparedDependencyHash)throw new CsoError('ASSERTION_FAILED','Project-installed test toolchain bytes changed between source phases');
}
const transformations=params.manifest.entries.filter(e=>e.transformation),transformationsHash=sha256(canonical(transformations)),archivesHash=sha256(canonical([...params.archives].sort())),requestHash=sha256(canonical(identityRequest)),preparationHash=params.preparation?sha256(canonical(params.preparation)):undefined,startPlanHash=params.startPlanHash??sha256(canonical(request.start)),testPlanHash=params.testPlanHash??sha256(canonical({commands:request.existingTests,files:[...request.testFiles].sort()})),auditPolicyHash=params.auditPolicyHash??sha256(canonical({})),assertionHash=sha256(canonical({legitimate:identityRequest.legitimate,security:identityRequest.security})),minimumPassingTests=params.minimumPassingTests??request.existingTests.map(()=>1),runner={testToolchain:params.testToolchain,startPlanHash,testPlanHash,commandsHash:sha256(canonical(request.existingTests)),minimumPassingTestsHash:sha256(canonical(minimumPassingTests))};
let assertionAssurance:VerificationManifest['assertionAssurance'],witnessHash:string|undefined;
if(params.witness){
const beforeReceipt=validateStoredAssertionWitnessReceipt(params.witness.before),afterReceipt=validateStoredAssertionWitnessReceipt(params.witness.after),stable=(binding:AssertionWitnessBinding)=>{const {nonce:_,issuedAt:__,expiresAt:___,...value}=binding;return value;},expected=(phase:'before'|'after',sourceHash:string,dependencyHash:string,configurationHash:string)=>({schemaVersion:1,protocol:'gstack-cso-assertion-witness-v1',phase,runId:params.runId,findingId:identityRequest.findingId,policyHash:params.policyHash,auditPolicyHash,runtime:{image:params.runtime.image,verifierImage:params.verifier.image,platform:params.runtime.platform,profile:params.runtime.id},runner,sourceHash,dependencyHash,configurationHash,requestHash,patchHash:pHash,harnessHash,assertionHash,fixturesHash});
if(canonical(stable(beforeReceipt.binding))!==canonical(expected('before',params.manifest.executionHash,dependenciesBefore,configurationBefore))||canonical(stable(afterReceipt.binding))!==canonical(expected('after',sourceAfter,dependenciesAfter,configurationAfter))||beforeReceipt.publicKey!==afterReceipt.publicKey||beforeReceipt.keyId!==afterReceipt.keyId||beforeReceipt.binding.nonce===afterReceipt.binding.nonce)throw new CsoError('INCOMPATIBLE_INPUT','Authenticated assertion witness receipts do not bind this verification');
const beforeObservation={...params.before,existingTests:beforeReceipt.diagnosticTestsPassed,inputHash:harnessHash},afterObservation={...params.after,existingTests:afterReceipt.diagnosticTestsPassed,inputHash:harnessHash};
if(beforeReceipt.observationHash!==witnessObservationHash(beforeObservation)||afterReceipt.observationHash!==witnessObservationHash(afterObservation)||params.before.existingTests!==beforeReceipt.diagnosticTestsPassed||params.after.existingTests!==afterReceipt.diagnosticTestsPassed||!beforeReceipt.externalAssertionsPassed||!afterReceipt.externalAssertionsPassed)throw new CsoError('INCOMPATIBLE_INPUT','Authenticated assertion witness receipts do not match the verifier observations');
const stableExecutions=(receipt:AssertionWitnessReceipt)=>receipt.executions.map(({outputHash:_,...execution})=>execution);
if(canonical(stableExecutions(beforeReceipt))!==canonical(stableExecutions(afterReceipt)))throw new CsoError('ASSERTION_FAILED','Repair changed the existing-test execution count or outcome');
assertionAssurance='authenticated_out_of_process';witnessHash=assertionWitnessPairHash({before:beforeReceipt,after:afterReceipt});
}
const passed=mechanical&&reviewGate&&assertionAssurance==='authenticated_out_of_process',preservationUnattested=mechanical&&reviewGate&&!passed,createdAt=new Date().toISOString(),verification:VerificationManifest={version:3,id:'',runId:params.runId,findingId:identityRequest.findingId,createdAt,helperAbi:3,runtime:{image:params.runtime.image,platform:params.runtime.platform,profile:params.runtime.id},testToolchain:params.testToolchain,policyHash:params.policyHash,auditPolicyHash,harnessHash,requestHash,startPlanHash,testPlanHash,fixturesHash,patchHash:pHash,originalSourceHash:params.manifest.originalHash,transformationsHash,archivesHash,...(preparationHash?{preparationHash}:{}),beforeSourceHash:params.manifest.executionHash,afterSourceHash:sourceAfter,beforeDependencies:dependenciesBefore,afterDependencies:dependenciesAfter,beforeConfiguration:configurationBefore,afterConfiguration:configurationAfter,before:{...params.before,inputHash:harnessHash},after:{...params.after,inputHash:harnessHash},review:identityRequest.review,reviewAssurance,...(assertionAssurance?{assertionAssurance}:{}),testCompletionAssurance,...(witnessHash?{witnessHash}:{}),result:passed?'runtime_tested':(inconclusive||preservationUnattested)?'inconclusive':'failed'};const id=verificationIdentity(verification);verification.id=id;
if(!['runtime_tested','tested'].includes(verification.result))return{manifest:verification};
const bundle:RepairBundle={schemaVersion:3,runId:params.runId,id,createdAt,expiresAt:new Date(Date.parse(createdAt)+30*86400_000).toISOString(),requiredInputs:{sourceHash:params.manifest.executionHash,originalHash:params.manifest.originalHash,runtimeImage:params.runtime.image,platform:params.runtime.platform,archives:params.archives,...(params.dependencyClosures?{dependencyClosures:params.dependencyClosures}:{})},request:identityRequest,verification,transformations,...(params.preparation?{preparation:params.preparation}:{}),...(params.reviewArtifact?{reviewArtifact:params.reviewArtifact}:{}),witness:params.witness!};
return{manifest:verification,bundle};
}
export function validateRepairBundle(value:unknown,id:string,sourceRoot?:string,sourceManifest?:SnapshotManifest):RepairBundle{
const bundle=value as RepairBundle;if(!bundle||bundle.schemaVersion!==3||bundle.id!==id||bundle.runId!==bundle.verification?.runId||bundle.verification?.id!==id||verificationIdentity(bundle.verification)!==id)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle identity or verification provenance is invalid');
const request=validateVerificationRequest(bundle.request),verification=bundle.verification,required=bundle.requiredInputs;
const createdAtMs=Date.parse(bundle.createdAt);if(!Number.isFinite(createdAtMs)||new Date(createdAtMs).toISOString()!==bundle.createdAt||bundle.createdAt!==verification.createdAt||bundle.expiresAt!==new Date(createdAtMs+30*86400_000).toISOString())throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle retention timestamps do not match their authenticated verification time');
if(!['runtime_tested','tested'].includes(verification.result)||!['self_attested','host_verified'].includes(verification.reviewAssurance)||verification.assertionAssurance!=='authenticated_out_of_process'||(verification.result==='tested'&&verification.testCompletionAssurance!=='authenticated_out_of_process')||(verification.result==='runtime_tested'&&verification.testCompletionAssurance!=='self_reported'))throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle lacks the assurance required by its repair label');
if(!['runtime','project'].includes(verification.testToolchain))throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle test toolchain provenance is invalid');
if(request.findingId!==verification.findingId||request.runtimeProfile!==verification.runtime.profile||sha256(canonical(request))!==verification.requestHash||canonical(request.review)!==canonical(verification.review)||patchHash(request)!==verification.patchHash||![verification.requestHash,verification.startPlanHash,verification.testPlanHash].every(value=>/^[a-f0-9]{64}$/.test(value))||sha256(canonical(request.fixtures))!==verification.fixturesHash||required.sourceHash!==verification.beforeSourceHash||required.originalHash!==verification.originalSourceHash||required.runtimeImage!==verification.runtime.image||required.platform!==verification.runtime.platform||sha256(canonical([...(required.archives??[])].sort()))!==verification.archivesHash||sha256(canonical(bundle.transformations??[]))!==verification.transformationsHash)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle request, harness, or contents do not match their authenticated manifest');
if(!bundle.witness||!verification.witnessHash)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle omitted its authenticated external assertion witness');
const beforeWitness=validateStoredAssertionWitnessReceipt(bundle.witness.before),afterWitness=validateStoredAssertionWitnessReceipt(bundle.witness.after),stable=(binding:AssertionWitnessBinding)=>{const {nonce:_,issuedAt:__,expiresAt:___,...rest}=binding;return rest;},assertionHash=sha256(canonical({legitimate:request.legitimate,security:request.security}));
if(assertionWitnessPairHash({before:beforeWitness,after:afterWitness})!==verification.witnessHash||beforeWitness.publicKey!==afterWitness.publicKey||beforeWitness.keyId!==afterWitness.keyId||beforeWitness.binding.nonce===afterWitness.binding.nonce)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle assertion witness identity is invalid');
for(const [phase,receipt,observation,sourceHash,dependencyHash,configurationHash] of [['before',beforeWitness,verification.before,verification.beforeSourceHash,verification.beforeDependencies,verification.beforeConfiguration],['after',afterWitness,verification.after,verification.afterSourceHash,verification.afterDependencies,verification.afterConfiguration]] as const){
const binding=stable(receipt.binding);if(binding.phase!==phase||binding.runId!==verification.runId||binding.findingId!==verification.findingId||binding.policyHash!==verification.policyHash||binding.auditPolicyHash!==verification.auditPolicyHash||binding.runtime.image!==verification.runtime.image||binding.runtime.verifierImage!==verification.runtime.image||binding.runtime.platform!==verification.runtime.platform||binding.runtime.profile!==verification.runtime.profile||binding.runner.testToolchain!==verification.testToolchain||binding.runner.startPlanHash!==verification.startPlanHash||binding.runner.testPlanHash!==verification.testPlanHash||binding.sourceHash!==sourceHash||binding.dependencyHash!==dependencyHash||binding.configurationHash!==configurationHash||binding.requestHash!==verification.requestHash||binding.patchHash!==verification.patchHash||binding.harnessHash!==verification.harnessHash||binding.assertionHash!==assertionHash||binding.fixturesHash!==verification.fixturesHash||receipt.observationHash!==witnessObservationHash(observation)||receipt.diagnosticTestsPassed!==observation.existingTests||!receipt.externalAssertionsPassed)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle assertion witness does not bind its verification manifest');
}
if(canonical(beforeWitness.binding.runner)!==canonical(afterWitness.binding.runner))throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle changed the witnessed runner between phases');
if(required.dependencyClosures){const hashes=new Set<string>();for(const phase of ['before','after'] as const){const closure=required.dependencyClosures[phase] as any;if(!closure||typeof closure!=='object'||Array.isArray(closure)||!Array.isArray(closure.archives)||!/^([a-f0-9]{64})$/.test(closure.closureHash??''))throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle dependency closure is malformed');const {closureHash,...body}=closure;if(sha256(canonical(body))!==closureHash)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle dependency closure identity is invalid');for(const archive of closure.archives){if(!archive||typeof archive!=='object'||!/^[a-f0-9]{64}$/.test(archive.sha256??''))throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle dependency archive provenance is malformed');hashes.add(archive.sha256);}}if(canonical([...hashes].sort())!==canonical([...(required.archives??[])].sort()))throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle archive hashes do not match its dependency closures');}
if(required.dependencyClosures&&!bundle.preparation)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle omitted prepared-source invariance proofs');
if(verification.testToolchain==='project'&&!bundle.preparation)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle omitted project test-toolchain preparation proofs');
if(bundle.preparation){if(!verification.preparationHash||sha256(canonical(bundle.preparation))!==verification.preparationHash||canonical(bundle.preparation.before?.transformations)!==canonical(bundle.preparation.after?.transformations)||bundle.preparation.before?.databaseHash!==bundle.preparation.after?.databaseHash)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle preparation proof is invalid');for(const phase of ['before','after'] as const){const proof=bundle.preparation[phase] as PreparationProof,closure=(required.dependencyClosures as any)?.[phase];if(!proof||proof.schemaVersion!==1||![proof.dependencyClosureHash,proof.configurationHash,proof.sourceProjectionHash,proof.preparedManifestHash,proof.preparedDependencyHash,proof.receiptHash,proof.executionEnvironmentHash,proof.databaseHash].every(value=>/^[a-f0-9]{64}$/.test(value))||!Array.isArray(proof.transformations)||proof.transformations.some(item=>!item||typeof item.path!=='string'||!/^[a-f0-9]{64}$/.test(item.sha256)||!Number.isInteger(item.mode)||typeof item.reason!=='string')||(closure&&proof.dependencyClosureHash!==closure.closureHash))throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle preparation proof does not bind its dependency closure');}}
if(verification.testToolchain==='project'&&bundle.preparation!.before.preparedDependencyHash!==bundle.preparation!.after.preparedDependencyHash)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle project test toolchain changed between source phases');
if(request.review.artifactId){if(!bundle.reviewArtifact)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle omitted its independent review artifact');validateReviewArtifact(bundle.reviewArtifact,bundle.runId,request);}
validateVerificationObservation(verification.before);validateVerificationObservation(verification.after);
if(sourceRoot){
const references=[...request.boundaryFiles,...request.testFiles,...request.changes.map(change=>change.path),...request.start.args,...request.existingTests.flatMap(command=>command.args)],hasHandles=references.some(reference=>Boolean(snapshotPathHandleId(reference)||reference.startsWith('./')&&snapshotPathHandleId(reference.slice(2))));
if(hasHandles&&!sourceManifest)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle path handles require the matching snapshot manifest for source validation');
const executionRequest=sourceManifest?resolveVerificationRequestPaths(sourceManifest,request):request;
if(verificationHarnessHash(executionRequest,sourceRoot)!==verification.harnessHash)throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle harness does not match supplied source inputs');
const commandsHash=sha256(canonical(executionRequest.existingTests)),commandHashes=executionRequest.existingTests.map(command=>sha256(canonical(command)));if(beforeWitness.binding.runner.commandsHash!==commandsHash||canonical(beforeWitness.executions.map(item=>item.commandHash))!==canonical(commandHashes)||canonical(afterWitness.executions.map(item=>item.commandHash))!==canonical(commandHashes))throw new CsoError('INCOMPATIBLE_INPUT','Repair bundle witnessed a different test runner command set');
}
return{...bundle,request};
}
export class DockerVerificationExecutor implements VerificationExecutor{
private attemptDeadline:number;private executionStarted=false;
constructor(private endpoint:DockerEndpoint,private watchdogPath:string,private runExecutionDeadline=Date.now()+300_000,private onExecutionStarted?:()=>void|Promise<void>){this.attemptDeadline=Math.min(Date.now()+300_000,runExecutionDeadline);}
async observe(source:string,phase:'before'|'after',request:VerificationRequest,runtime:QualifiedRuntime,verifier:QualifiedRuntime,work:string,control:string,execution?:{environment:Record<string,string>;database?:PreparedDatabaseContract},testEvidence?:{minimumPassingTests:number[]},witness?:AssertionWitnessHandle):Promise<VerificationObservation|WitnessedVerificationResult>{
const phaseDir=secureDirectory(join(work,phase)),phaseControl=secureDirectory(join(control,phase)),policy=secureDirectory(join(phaseDir,'policy')),policyFile=join(policy,'verification.json'),fixtures=secureDirectory(join(phaseDir,'fixtures'));
const verifierPolicy=JSON.stringify({phase,port:request.port,legitimate:request.legitimate,security:request.security});
if(redact(verifierPolicy)!==verifierPolicy)throw new CsoError('REDACTION_FAILED','Verification harness contains secret-bearing data');fs.writeFileSync(policyFile,verifierPolicy,{mode:0o600});
for(const [path,body] of Object.entries(request.fixtures)){const file=containedFile(fixtures,path);secureDirectory(dirname(file));fs.writeFileSync(file,body,{mode:0o600});}
const deadline=this.attemptDeadline;if(deadline<=Date.now())throw new CsoError('DEADLINE','No execution time remains before the reporting reserve');let group:DockerGroup|undefined;
try{
group=await DockerGroup.create(this.endpoint,`${request.findingId.slice(0,12)}-${phase}-${Date.now()}-${randomBytes(6).toString('hex')}`,phaseControl,deadline,verifier.image,this.watchdogPath);
if(!this.executionStarted){this.executionStarted=true;await this.onExecutionStarted?.();}
const supplied=execution?.environment??{},allowed=new Set(['PATH','VIRTUAL_ENV','PYTHONNOUSERSITE','BUNDLE_PATH','BUNDLE_FROZEN','BUNDLE_DEPLOYMENT','BUNDLE_DISABLE_SHARED_GEMS','BUNDLE_IGNORE_CONFIG','BUNDLE_ALLOW_OFFLINE_INSTALL','BUNDLE_CACHE_PATH','BUNDLE_USER_HOME','GEM_HOME','GEM_PATH']);if(Object.entries(supplied).some(([key,value])=>!allowed.has(key)||typeof value!=='string'||value.includes('\0')))throw new CsoError('ISOLATION_FAILED','Prepared execution environment exceeded its fixed allowlist');
const env={...supplied,PORT:String(request.port),HOST:'127.0.0.1',NODE_ENV:'test',RAILS_ENV:'test',RACK_ENV:'test',PYTHONUNBUFFERED:'1',CI:'1',SECRET_KEY_BASE:'cso-synthetic-test-key',CSO_FIXTURES:'/fixtures'};
const database=execution?.database;
if(database&&runtime.stack!=='rails')throw new CsoError('INCOMPATIBLE_INPUT','Prepared database contract can only execute with Rails');
if(runtime.stack==='rails'&&!database)throw new CsoError('PREREQUISITE','Rails verification omitted its prepared database contract');
if(database?.adapter==='postgresql'){
const databaseFile=join(policy,'postgresql.databases'),names=database.connections.map(name=>`cso_${name}`);
if(!names.length||names.some(name=>!/^cso_[A-Za-z_][A-Za-z0-9_]{0,47}$/.test(name)))throw new CsoError('INCOMPATIBLE_INPUT','Prepared PostgreSQL connection names are invalid');
fs.writeFileSync(databaseFile,names.join('\n')+'\n',{mode:0o444,flag:'wx'});
const postgres=await group.createContainer({role:'postgres',image:database.sidecar.image,
command:['/opt/cso/run-postgresql','/policy/postgresql.databases'],postgresDatabasePolicy:databaseFile});
await group.start(postgres);let ready=false;
for(let attempt=0;attempt<100&&!ready;attempt++){
const checked=await group.execCapture(postgres,['/opt/cso/postgresql-ready','/policy/postgresql.databases']);
ready=checked.code===0;if(!ready)await new Promise(resolve=>setTimeout(resolve,50));
}
if(!ready)throw new CsoError('TOOL_FAILED','Disposable PostgreSQL did not create and accept connections for every declared Rails database');
}
const cleanCommand=(command:string[])=>['/usr/bin/env','-i',...Object.entries(env).sort(([a],[b])=>a.localeCompare(b)).map(([key,value])=>`${key}=${value}`),...command];
const dbPrepare=async(id:string)=>{
const result=await group!.execCapture(id,cleanCommand(['/usr/local/bin/bundle','exec','rails','db:prepare']),{workdir:'/work'});
if(result.code!==0)throw new CsoError('TOOL_FAILED','Rails database preparation failed in the isolated test environment');
};
let app:string;
if(runtime.stack==='rails'){
app=await group.createContainer({role:'app',image:runtime.image,source,env,command:['/opt/cso/run-app','/bin/sleep','2147483647'],readonlyDirectories:[{host:fixtures,container:'/fixtures'}]});
await group.start(app);await dbPrepare(app);await group.execDetached(app,[request.start.executable,...request.start.args]);
}else{
app=await group.createContainer({role:'app',image:runtime.image,source,env,command:['/opt/cso/run-app',request.start.executable,...request.start.args],readonlyDirectories:[{host:fixtures,container:'/fixtures'}]});await group.start(app);
}
const verifierId=await group.createContainer({role:'verifier',image:verifier.image,command:['/opt/cso/verifier','/policy/verification.json'],readonlyFiles:[{host:policyFile,container:'/policy/verification.json'}]});
const result=await group.startAttach(verifierId);let observation:VerificationObservation;
try{observation=validateVerificationObservation(JSON.parse(result.output.trim()));}catch{observation={booted:false,legitimate:false,security:'inconclusive',existingTests:false,output:'verifier returned invalid bounded output',inputHash:''};}
await group.removeContainer(verifierId);
await group.removeContainer(app);
const minimumPassingTests=testEvidence?.minimumPassingTests??request.existingTests.map(()=>1);if(minimumPassingTests.length!==request.existingTests.length||minimumPassingTests.some(value=>!Number.isInteger(value)||value<1))throw new CsoError('INCOMPATIBLE_INPUT','Helper-derived test execution-count floors do not match the canonical test commands');
let existingTests=true;const executions:Array<{command:VerificationRequest['existingTests'][number];code:number;output:string;minimumPassingTests:number}>=[];for(const [index,test] of request.existingTests.entries()){
const rails=runtime.stack==='rails';
const testId=await group.createContainer({role:'tests',image:runtime.image,source,env,
command:rails?['/opt/cso/run-app','/bin/sleep','2147483647']:['/opt/cso/run-app',test.executable,...test.args],readonlyDirectories:[{host:fixtures,container:'/fixtures'}]});
let testResult:{code:number;output:string};
if(rails){await group.start(testId);await dbPrepare(testId);const result=await group.execCapture(testId,cleanCommand([test.executable,...test.args]),{workdir:'/work'});testResult={code:result.code,output:result.stdout+result.stderr};}
else testResult=await group.startAttach(testId);
executions.push({command:test,code:testResult.code,output:testResult.output,minimumPassingTests:minimumPassingTests[index]});if(!testExecutionPassed(test,testResult.code,testResult.output,minimumPassingTests[index]))existingTests=false;await group.removeContainer(testId);
}
if(witness){if(observation.existingTests)throw new CsoError('INCOMPATIBLE_INPUT','External verifier attempted to assert project test completion');const receipt=await witness.attest(observation,executions),witnessed={...observation,existingTests:receipt.diagnosticTestsPassed,inputHash:witness.binding.harnessHash};return{observation:witnessed,witness:receipt};}
observation.existingTests=existingTests;return observation;
}finally{if(group)await group.cleanup();}
}
}
export async function verifyRepair(params:{runId:string;runDir:string;manifest:SnapshotManifest;rawRequest:unknown;runtime:QualifiedRuntime;verifier:QualifiedRuntime;policyHash:string;auditPolicyHash?:string;archives:string[];dependencyClosures?:{before:unknown;after:unknown};preparation?:{before:PreparationProof;after:PreparationProof};reviewArtifact?:RepairReviewArtifact;executor:VerificationExecutor;persist?:boolean;watchdogPath?:string;attemptDeadline?:number}):Promise<{manifest:VerificationManifest;bundle:RepairBundle}>{
const identityRequest=validateVerificationRequest(params.rawRequest),request=resolveVerificationRequestPaths(params.manifest,identityRequest),snapshot=join(params.runDir,'snapshot'),work=join(params.runDir,'verification',`${request.findingId}-${Date.now()}-${randomBytes(4).toString('hex')}`),after=join(work,'sources','after'),observations=join(work,'observations'),groupControls=secureDirectory(join(params.runDir,'supervision',basename(work),'docker-groups'));
if(canonical(sanitizeHelperForJson(identityRequest))!==canonical(identityRequest))throw new CsoError('REDACTION_FAILED','Verification request contains sensitive material that cannot enter a replayable bundle');
if(!['node','bun','python','rails'].includes(params.runtime.stack))throw new CsoError('INCOMPATIBLE_INPUT','Canonical project tests require an application runtime');const stack=params.runtime.stack as CsoStack,beforeTestPlan=assertCanonicalTestPlan(request,snapshot,stack),beforeStartPlan=assertCanonicalStartPlan(request,snapshot,stack);
if(beforeTestPlan.toolchain==='project'&&request.changes.some(change=>change.effect==='dependency'))throw new CsoError('PREREQUISITE','Runtime-tested dependency repairs require a test runner pinned in the qualified runtime; project-installed test toolchains may change with the repair');
for(const boundary of [...new Set([...request.boundaryFiles,...request.testFiles,...beforeStartPlan.entrypointFiles])]){const e=params.manifest.entries.find(x=>x.path===boundary);if(!e||e.transformation)throw new CsoError('INCOMPATIBLE_INPUT',`Snapshot transformation changes or withholds a verification input: ${boundary}`);}
for(const change of request.changes){const path=relativePath(change.path),entry=params.manifest.entries.find(x=>x.path===path);containedFile(snapshot,path);if(change.beforeSha256===null){if(entry)throw new CsoError('INCOMPATIBLE_INPUT',`Declared new repair path already exists in the snapshot: ${path}`);}else if(!entry||entry.transformation)throw new CsoError('INCOMPATIBLE_INPUT',`Snapshot transformation changes or withholds a repair input: ${path}`);}
let guardedCleanup:(()=>Promise<void>)|undefined;if(params.watchdogPath){const deadline=Math.min(params.attemptDeadline??Date.now()+300_000,Date.now()+300_000);guardedCleanup=await attemptGuard(params.runDir,work,params.watchdogPath,deadline);}secureDirectory(observations);
let certified:ReturnType<typeof certify>|undefined,beforeObs:VerificationObservation|undefined,afterObs:VerificationObservation|undefined,beforeWitness:AssertionWitnessReceipt|undefined,afterWitness:AssertionWitnessReceipt|undefined,failure:unknown,missingExternalWitness=false;
try{
preparePatchedSource(snapshot,after,request);
const afterTestPlan=canonicalTestPlan(after,stack),afterStartPlan=canonicalStartPlan(after,stack,request.port);if(canonical(afterTestPlan)!==canonical(beforeTestPlan))throw new CsoError('ASSERTION_FAILED','Repair changed the canonical project test suite, runner configuration, or discovered test inputs');
const startShape=(plan:CanonicalStartPlan)=>({command:plan.command,kind:plan.kind,entrypointFiles:plan.entrypointFiles}),changedPaths=new Set(request.changes.map(change=>change.path));
if(canonical(startShape(afterStartPlan))!==canonical(startShape(beforeStartPlan))||(afterStartPlan.signature!==beforeStartPlan.signature&&!beforeStartPlan.entrypointFiles.some(path=>changedPaths.has(path))))throw new CsoError('ASSERTION_FAILED','Repair changed the helper-derived application startup plan outside its declared patch');
const beforeInvariant=treeHash(snapshot),afterInvariant=treeHash(after);if(beforeInvariant!==params.manifest.executionHash)throw new CsoError('INCOMPATIBLE_INPUT','Retained source does not match the snapshot identity bound to this verification');
const testEvidence={minimumPassingTests:beforeTestPlan.minimumPassingTests},auditPolicyHash=params.auditPolicyHash??sha256(canonical({})),requestHash=sha256(canonical(identityRequest)),pHash=patchHash(identityRequest),harnessHash=verificationHarnessHash(request,snapshot),assertionHash=sha256(canonical({legitimate:identityRequest.legitimate,security:identityRequest.security})),runner={testToolchain:beforeTestPlan.toolchain,startPlanHash:beforeStartPlan.signature,testPlanHash:beforeTestPlan.signature,commandsHash:sha256(canonical(request.existingTests)),minimumPassingTestsHash:sha256(canonical(beforeTestPlan.minimumPassingTests))},session=new AssertionWitnessSession(observations,Math.min(params.attemptDeadline??Date.now()+300_000,Date.now()+300_000)),stable=(phase:'before'|'after',root:string,sourceHash:string):Omit<AssertionWitnessBinding,'schemaVersion'|'protocol'|'nonce'|'issuedAt'|'expiresAt'>=>({phase,runId:params.runId,findingId:identityRequest.findingId,policyHash:params.policyHash,auditPolicyHash,runtime:{image:params.runtime.image,verifierImage:params.verifier.image,platform:params.runtime.platform,profile:params.runtime.id},runner,sourceHash,dependencyHash:treeHash(root,p=>DEPENDENCY.test(p)),configurationHash:treeHash(root,p=>CONFIG.test(p)&&!DEPENDENCY.test(p)),requestHash,patchHash:pHash,harnessHash,assertionHash,fixturesHash:sha256(canonical(identityRequest.fixtures))}),beforeHandle=session.handle(stable('before',snapshot,beforeInvariant));
const rawBefore=await params.executor.observe(snapshot,'before',request,params.runtime,params.verifier,observations,groupControls,undefined,testEvidence,beforeHandle),observedBefore='observation'in(rawBefore as any)?validateVerificationObservation((rawBefore as WitnessedVerificationResult).observation):validateVerificationObservation(rawBefore as VerificationObservation);
if('observation'in(rawBefore as any))beforeWitness=beforeHandle.validate((rawBefore as WitnessedVerificationResult).witness,observedBefore);
if(treeHash(snapshot)!==beforeInvariant)throw new CsoError('ASSERTION_FAILED','Verification mutated the retained source snapshot');
beforeObs=observedBefore;
const afterHandle=session.handle(stable('after',after,afterInvariant)),rawAfter=await params.executor.observe(after,'after',request,params.runtime,params.verifier,observations,groupControls,undefined,testEvidence,afterHandle),observedAfter='observation'in(rawAfter as any)?validateVerificationObservation((rawAfter as WitnessedVerificationResult).observation):validateVerificationObservation(rawAfter as VerificationObservation);
if('observation'in(rawAfter as any))afterWitness=afterHandle.validate((rawAfter as WitnessedVerificationResult).witness,observedAfter);
if(treeHash(after)!==afterInvariant)throw new CsoError('ASSERTION_FAILED','Verification mutated the pristine patched source');
afterObs=observedAfter;
certified=certify({...params,request,identityRequest,before:beforeObs,after:afterObs,beforeRoot:snapshot,afterRoot:after,startPlanHash:beforeStartPlan.signature,testPlanHash:beforeTestPlan.signature,testToolchain:beforeTestPlan.toolchain,minimumPassingTests:beforeTestPlan.minimumPassingTests,...(beforeWitness&&afterWitness?{witness:{before:beforeWitness,after:afterWitness}}:{})});
}catch(error){failure=error;}
try{if(guardedCleanup)await guardedCleanup();else fs.rmSync(work,{recursive:true,force:true});}catch(error){failure=error;certified=undefined;missingExternalWitness=false;}
if(!failure&&certified&&!certified.bundle){const manifest=certified.manifest,review=manifest.review;missingExternalWitness=!manifest.assertionAssurance&&manifest.testCompletionAssurance==='self_reported'&&manifest.result==='inconclusive'&&manifest.before.booted&&manifest.before.legitimate&&manifest.before.security==='intended_failure'&&manifest.before.existingTests&&manifest.after.booted&&manifest.after.legitimate&&manifest.after.security==='pass'&&manifest.after.existingTests&&review.independent&&review.rootCauseRepaired&&review.featurePreserved&&!review.boundaryMocks;failure=missingExternalWitness?new CsoError('PREREQUISITE','Repair verification retained self-reported project-test diagnostics but requires a helper-authenticated out-of-process external assertion witness before a runtime-tested bundle can be issued'):new CsoError('ASSERTION_FAILED',manifest.result==='inconclusive'?'Repair verification was inconclusive; no repair bundle was issued':'Repair failed one or more required boot, control, security, existing-test, or review assertions; no repair bundle was issued');}
if(failure){
if(beforeObs){
const cause=failure instanceof CsoError?failure:new CsoError('ASSERTION_FAILED','Repair validation failed after the before-phase observation');
const reproduction=!beforeObs.booted?'blocked':!beforeObs.legitimate?'inconclusive':beforeObs.security==='intended_failure'?'reproduced':beforeObs.security==='pass'?'disproved':'inconclusive',harnessHash=verificationHarnessHash(request,snapshot);
const missingWitness=missingExternalWitness&&cause.code==='PREREQUISITE'&&reproduction==='reproduced'&&afterObs?.booted===true&&afterObs.legitimate===true&&afterObs.security==='pass'&&beforeObs.existingTests&&afterObs.existingTests;
const raw={schemaVersion:3 as const,artifactKind:'repair_candidate' as const,runId:params.runId,findingId:identityRequest.findingId,createdAt:new Date().toISOString(),bundleIssued:false as const,runtime:{image:params.runtime.image,platform:params.runtime.platform,profile:params.runtime.id},policyHash:params.policyHash,requestHash:sha256(canonical(identityRequest)),harnessHash,sourceHash:params.manifest.executionHash,request:identityRequest,patchHash:patchHash(identityRequest),testToolchain:beforeTestPlan.toolchain,testCompletionAssurance:'self_reported' as const,...(params.preparation?{preparationHash:sha256(canonical(params.preparation))}:{}),before:{...beforeObs,inputHash:harnessHash},...(afterObs?{after:{...afterObs,inputHash:harnessHash}}:{}),reproduction,repair:missingWitness?'proposed' as const:'failed' as const,failure:{code:cause.code,message:cause.message}},safe=sanitizeHelperForJson(raw) as Omit<FailedVerificationAttempt,'id'>,id=sha256(canonical(safe)).slice(0,32),attempt:FailedVerificationAttempt={...safe,id};
validateVerificationObservation(attempt.before);if(attempt.after)validateVerificationObservation(attempt.after);if(params.persist!==false)writeJsonExclusive(join(params.runDir,'verification-attempts',`${id}.json`),attempt);
throw new VerificationAttemptError(cause,attempt);
}
throw failure;
}
if(!certified?.bundle)throw new CsoError('ASSERTION_FAILED','Verification ended without a certifiable result');
if(params.persist!==false){const persistable=sanitizeHelperForJson(certified.bundle);if(canonical(persistable)!==canonical(certified.bundle))throw new CsoError('REDACTION_FAILED','Repair bundle provenance contains material that cannot be persisted without changing its identity');validateRepairBundle(persistable,certified.bundle.id,snapshot,params.manifest);writeJsonExclusive(join(params.runDir,'bundles',`${certified.bundle.id}.json`),persistable);}
return certified;
}