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>
This commit is contained in:
Garry Tan
2026-09-14 15:14:58 -07:00
committed by GitHub
co-authored by OpenAI Codex
parent 9f81911136
commit 4a3c6a8a3c
160 changed files with 24697 additions and 2288 deletions
+4 -1
View File
@@ -296,7 +296,10 @@ describe('web research ({{ASIDE_RESEARCH}})', () => {
});
test('every template carrying {{ASIDE_RESEARCH}} renders the section exactly once', () => {
expect(carriers).toEqual(expect.arrayContaining(['cso', 'design-consultation', 'investigate', 'office-hours', 'plan-ceo-review', 'plan-devex-review', 'plan-eng-review', 'review']));
// CSO's private startup keeps advisory queries inside its stricter public-ID
// policy and intentionally does not import the generic Aside research block.
expect(carriers).not.toContain('cso');
expect(carriers).toEqual(expect.arrayContaining(['design-consultation', 'investigate', 'office-hours', 'plan-ceo-review', 'plan-devex-review', 'plan-eng-review', 'review']));
for (const skill of carriers) {
const md = fs.readFileSync(path.join(ROOT, skill, 'SKILL.md'), 'utf-8');
expect({ skill, count: md.split('## Web research runs in Aside').length - 1 }).toEqual({ skill, count: 1 });
+2 -1
View File
@@ -107,7 +107,8 @@ const EXPECTED_INTERACTIVE = [
'qa-only',
'codex',
'autoplan',
'cso',
// CSO uses a private startup and intentionally omits the shared PREAMBLE,
// including its generic AskUserQuestion formatting block.
'investigate',
'retro',
'design-review',
+17
View File
@@ -0,0 +1,17 @@
import { afterEach, describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { readBoundedStable } from '../lib/cso/bounded-file';
const roots:string[]=[];afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
describe('CSO caller control-file reader',()=>{
test.skipIf(process.platform==='win32')('cannot block on a FIFO raced over a validated regular file',()=>{
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-control-file-'));roots.push(root);const input=path.join(root,'request.json'),original=path.join(root,'request.original'),fifo=path.join(root,'request.fifo');fs.writeFileSync(input,'{}\n');expect(spawnSync('/usr/bin/mkfifo',[fifo],{timeout:5_000}).status).toBe(0);const open=fs.openSync;let checked=false;
const patched=spyOn(fs,'openSync').mockImplementation(((candidate:any,flags:any,mode?:any)=>{if(String(candidate)===input){checked=true;if((Number(flags)&(fs.constants.O_NONBLOCK??0))===0)throw new Error('reader would block on raced FIFO');fs.renameSync(input,original);fs.renameSync(fifo,input);}return mode===undefined?open(candidate,flags):open(candidate,flags,mode);}) as typeof fs.openSync);
try{expect(()=>readBoundedStable(input,1024,'Input file')).toThrow('changed before it could be read');}finally{patched.mockRestore();}
expect(checked).toBe(true);
});
});
+280
View File
@@ -0,0 +1,280 @@
import { afterEach, describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { createHash } from 'node:crypto';
import { PublicArchiveCache } from '../lib/cso/cache';
import { CsoError } from '../lib/cso/contracts';
const roots: string[] = [];
function fixture(maxBytes = 1024, now: () => number = Date.now) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cso-cache-'));
roots.push(root);
const staging = path.join(root, 'staging'), cacheRoot = path.join(root, 'cache');
fs.mkdirSync(staging, { mode: 0o700 });
return { root, staging, cacheRoot, cache: new PublicArchiveCache({ root: cacheRoot, stagingRoot: staging, maxBytes, now }) };
}
function stage(staging: string, name: string, value: string | Buffer): { path: string; digest: string } {
const file = path.join(staging, name);
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
fs.writeFileSync(file, value, { mode: 0o600 });
return { path: name, digest: createHash('sha256').update(value).digest('hex') };
}
function code(fn: () => unknown): string | undefined {
try { fn(); return undefined; }
catch (error) { return error instanceof CsoError ? error.code : undefined; }
}
afterEach(() => { for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); });
describe('CSO immutable public archive cache', () => {
test('promotes only matching bytes and verifies the full hash on every hit', () => {
let tick = 100;
const { staging, cacheRoot, cache } = fixture(1024, () => tick++);
const archive = stage(staging, 'pkg.tgz', 'verified-public-archive');
const entry = cache.promote(archive.path, archive.digest);
expect(fs.readFileSync(entry.path, 'utf8')).toBe('verified-public-archive');
expect(fs.statSync(cacheRoot).mode & 0o777).toBe(0o700);
expect(fs.statSync(entry.path).mode & 0o777).toBe(0o400);
expect(fs.statSync(path.join(cacheRoot, 'metadata', `${archive.digest}.json`)).mode & 0o777).toBe(0o600);
expect(cache.get(archive.digest)?.lastAccessedAt).toBeGreaterThan(entry.lastAccessedAt);
fs.chmodSync(entry.path, 0o600);
fs.writeFileSync(entry.path, 'poisoned-public-archive');
fs.chmodSync(entry.path, 0o400);
expect(code(() => cache.get(archive.digest))).toBe('INCOMPATIBLE_INPUT');
});
test('rejects traversal, symlinks, hard links, directories, oversized files, and hash mismatches', () => {
const { root, staging, cache } = fixture(16);
const good = stage(staging, 'good', 'good');
expect(code(() => cache.promote('../good', good.digest))).toBe('UNSAFE_PATH');
fs.symlinkSync(path.join(staging, 'good'), path.join(staging, 'link'));
expect(code(() => cache.promote('link', good.digest))).toBe('UNSAFE_PATH');
fs.linkSync(path.join(staging, 'good'), path.join(staging, 'hard'));
expect(code(() => cache.promote('hard', good.digest))).toBe('UNSAFE_PATH');
fs.unlinkSync(path.join(staging, 'hard'));
fs.mkdirSync(path.join(staging, 'directory'));
expect(code(() => cache.promote('directory', good.digest))).toBe('UNSAFE_PATH');
const large = stage(staging, 'large', Buffer.alloc(17, 1));
expect(code(() => cache.promote(large.path, large.digest))).toBe('INSUFFICIENT_CAPACITY');
expect(code(() => cache.promote(good.path, '0'.repeat(64)))).toBe('INCOMPATIBLE_INPUT');
expect(code(() => cache.promote('/absolute', good.digest))).toBe('UNSAFE_PATH');
expect(code(() => cache.promote(good.path, 'SHA256:bad'))).toBe('INVALID_ARGUMENT');
expect(fs.readdirSync(path.join(root, 'cache', 'entries'))).toEqual([]);
});
test('evicts least-recently-used entries within a configurable byte ceiling', () => {
let now = 0;
const { staging, cache } = fixture(8, () => ++now);
const first = stage(staging, 'first', '1111');
const second = stage(staging, 'second', '2222');
const third = stage(staging, 'third', '3333');
cache.promote(first.path, first.digest);
cache.promote(second.path, second.digest);
cache.get(first.digest);
cache.promote(third.path, third.digest);
expect(cache.get(first.digest)).toBeDefined();
expect(cache.get(second.digest)).toBeUndefined();
expect(cache.get(third.digest)).toBeDefined();
expect(cache.stats()).toEqual({ entries: 2, bytes: 8, maxBytes: 8 });
});
test('a hash-mismatched staging object cannot evict verified LRU entries', () => {
const { staging, cache } = fixture(8);
const first = stage(staging, 'first-kept', '1111');
const second = stage(staging, 'second-kept', '2222');
cache.promote(first.path, first.digest);
cache.promote(second.path, second.digest);
const invalid = stage(staging, 'invalid', '3333');
expect(code(() => cache.promote(invalid.path, '0'.repeat(64)))).toBe('INCOMPATIBLE_INPUT');
expect(cache.get(first.digest)).toBeDefined();
expect(cache.get(second.digest)).toBeDefined();
expect(cache.stats()).toEqual({ entries: 2, bytes: 8, maxBytes: 8 });
});
test('materializes one immutable run-owned set atomically before later eviction', () => {
const { root, staging, cache } = fixture(8);
const first = stage(staging, 'first-pin', '1111'), second = stage(staging, 'second-pin', '2222');
cache.promote(first.path, first.digest); cache.promote(second.path, second.digest);
const destination = path.join(root, 'run-owned'); fs.mkdirSync(destination, { mode: 0o700 });
const copies = cache.materialize([first.digest, second.digest], destination);
expect(copies.map(item => item.sha256)).toEqual([first.digest, second.digest].sort());
const third = stage(staging, 'third-pin', '3333'); cache.promote(third.path, third.digest);
expect(copies.every(item => fs.existsSync(item.path) && (fs.statSync(item.path).mode & 0o777) === 0o400)).toBe(true);
expect(copies.map(item => createHash('sha256').update(fs.readFileSync(item.path)).digest('hex')).sort()).toEqual([first.digest, second.digest].sort());
});
test('bounds every public operation by an optional absolute deadline', () => {
const { root, staging, cache } = fixture(1024);
const archive = stage(staging, 'deadline', 'bounded');
const expired = Date.now() - 1;
expect(code(() => cache.promote(archive.path, archive.digest, expired))).toBe('DEADLINE');
const entry = cache.promote(archive.path, archive.digest);
expect(code(() => cache.get(entry.sha256, { deadline: expired }))).toBe('DEADLINE');
expect(code(() => cache.stats({ deadline: expired }))).toBe('DEADLINE');
const destination = path.join(root, 'deadline-materialization'); fs.mkdirSync(destination, { mode: 0o700 });
expect(code(() => cache.materialize([entry.sha256], destination, { deadline: expired }))).toBe('DEADLINE');
expect(fs.readdirSync(destination)).toEqual([]);
});
test('cancels between archive-copy chunks and removes the partial incoming object', () => {
const { staging, cacheRoot, cache } = fixture(512 * 1024);
const archive = stage(staging, 'cancel-copy', Buffer.alloc(192 * 1024, 0x61));
const controller = new AbortController(), original = fs.writeSync.bind(fs); let archiveWrites = 0;
const writer = spyOn(fs, 'writeSync').mockImplementation(((fd: number, buffer: string | NodeJS.ArrayBufferView,
offsetOrPosition?: number | null, lengthOrEncoding?: number | BufferEncoding, position?: number | null) => {
const written = (original as any)(fd, buffer, offsetOrPosition, lengthOrEncoding, position);
if (Buffer.isBuffer(buffer) && lengthOrEncoding === 64 * 1024 && written > 0) { archiveWrites++; controller.abort(); }
return written;
}) as typeof fs.writeSync);
try {
expect(code(() => cache.promote(archive.path, archive.digest, { deadline: Date.now() + 60_000, signal: controller.signal }))).toBe('CANCELLED');
} finally { writer.mockRestore(); }
expect(archiveWrites).toBe(1);
expect(fs.readdirSync(path.join(cacheRoot, 'incoming'))).toEqual([]);
expect(fs.readdirSync(path.join(cacheRoot, 'entries'))).toEqual([]);
expect(fs.readdirSync(path.join(cacheRoot, 'metadata'))).toEqual([]);
});
test('cancels between cache-hit read chunks without damaging the immutable entry', () => {
const { staging, cacheRoot, cache } = fixture(512 * 1024);
const archive = stage(staging, 'cancel-read', Buffer.alloc(192 * 1024, 0x62)), entry = cache.promote(archive.path, archive.digest);
const controller = new AbortController(), original = fs.readSync.bind(fs); let archiveReads = 0;
const reader = spyOn(fs, 'readSync').mockImplementation(((fd: number, buffer: NodeJS.ArrayBufferView,
offset: number, length: number, position: number | null) => {
const read = original(fd, buffer, offset, length, position);
if (length === 64 * 1024 && read > 0) { archiveReads++; controller.abort(); }
return read;
}) as typeof fs.readSync);
try {
expect(code(() => cache.get(entry.sha256, { signal: controller.signal }))).toBe('CANCELLED');
} finally { reader.mockRestore(); }
expect(archiveReads).toBe(1);
expect(fs.existsSync(entry.path)).toBe(true);
expect(fs.readdirSync(path.join(cacheRoot, 'metadata'))).toEqual([`${entry.sha256}.json`]);
});
test('never overwrites an immutable entry and recovers interrupted entry publication', () => {
const { staging, cacheRoot, cache } = fixture();
const archive = stage(staging, 'one', 'same bytes');
const first = cache.promote(archive.path, archive.digest);
const inode = fs.statSync(first.path).ino;
stage(staging, 'two', 'same bytes');
const again = cache.promote('two', archive.digest);
expect(fs.statSync(again.path).ino).toBe(inode);
fs.unlinkSync(path.join(cacheRoot, 'metadata', `${archive.digest}.json`));
expect(cache.get(archive.digest)).toBeUndefined();
expect(fs.existsSync(first.path)).toBe(false);
expect(cache.promote(archive.path, archive.digest).sha256).toBe(archive.digest);
});
test('recovers dead immutable leases, never expires a live owner by age, and fences legacy directories', () => {
const stale = fixture();
stale.cache.stats();const staleLeases=path.join(stale.cacheRoot,'.mutation-lock-leases'),staleToken='a'.repeat(32),staleLease=path.join(staleLeases,`${staleToken}.json`);
fs.writeFileSync(staleLease,JSON.stringify({pid:2147483647,processIdentity:'linux:1',token:staleToken,createdAt:1})+'\n',{mode:0o600});
expect(stale.cache.stats()).toEqual({ entries: 0, bytes: 0, maxBytes: 1024 });
expect(fs.existsSync(staleLease)).toBe(false);expect(fs.lstatSync(path.join(stale.cacheRoot,'.lock')).isFile()).toBe(true);
const ownerless = fixture();
fs.mkdirSync(path.join(ownerless.cacheRoot, '.lock'), { mode: 0o700 });
const old = new Date(Date.now() - 60_000);
fs.utimesSync(path.join(ownerless.cacheRoot, '.lock'), old, old);
expect(code(()=>ownerless.cache.stats())).toBe('INSUFFICIENT_CAPACITY');expect(fs.lstatSync(path.join(ownerless.cacheRoot,'.lock')).isDirectory()).toBe(true);
const live = fixture();
live.cache.stats();const liveLeases=path.join(live.cacheRoot,'.mutation-lock-leases'),liveToken='b'.repeat(32),liveLease=path.join(liveLeases,`${liveToken}.json`);
fs.writeFileSync(liveLease,JSON.stringify({pid:process.pid,token:liveToken,createdAt:1})+'\n',{mode:0o600});fs.utimesSync(liveLease,old,old);
expect(code(() => live.cache.stats())).toBe('INSUFFICIENT_CAPACITY');
});
test('recovers an exact cache-protocol publication link and rejects an unrecognized one',()=>{
const recovered=fixture(),lock=path.join(recovered.cacheRoot,'.lock'),temporary=`${lock}.tmp.2147483647.deadbeef`;
fs.writeFileSync(lock,JSON.stringify({protocol:'immutable-cache-lease-set-v3'})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(lock,temporary);
expect(recovered.cache.stats()).toEqual({entries:0,bytes:0,maxBytes:1024});expect(fs.statSync(lock).nlink).toBe(1);expect(fs.existsSync(temporary)).toBe(false);
const poisoned=fixture(),poisonedLock=path.join(poisoned.cacheRoot,'.lock'),unrecognized=`${poisonedLock}.tmp.dead.bad`;
fs.writeFileSync(poisonedLock,JSON.stringify({protocol:'immutable-cache-lease-set-v3'})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(poisonedLock,unrecognized);
expect(code(()=>poisoned.cache.stats())).toBe('UNSAFE_PATH');expect(fs.statSync(poisonedLock).nlink).toBe(2);
});
test('recovers orphan metadata and transaction tombs but refuses poisoned recovery objects', () => {
const { root, cacheRoot, cache } = fixture();
const digest = 'c'.repeat(64);
fs.writeFileSync(path.join(cacheRoot, 'metadata', `${digest}.json`), JSON.stringify({
version: 1, sha256: digest, bytes: 1, createdAt: 1, lastAccessedAt: 1,
}), { mode: 0o600 });
const tomb = `.recovery-entry-${digest}-${process.pid}-${'d'.repeat(24)}`;
fs.writeFileSync(path.join(cacheRoot, 'recovery', tomb), 'interrupted', { mode: 0o400 });
expect(cache.stats().entries).toBe(0);
expect(fs.readdirSync(path.join(cacheRoot, 'metadata'))).toEqual([]);
expect(fs.readdirSync(path.join(cacheRoot, 'recovery'))).toEqual([]);
const outside = path.join(root, 'outside-recovery');
fs.writeFileSync(outside, 'untouched');
fs.symlinkSync(outside, path.join(cacheRoot, 'entries', digest));
expect(code(() => cache.get(digest))).toBe('UNSAFE_PATH');
expect(fs.readFileSync(outside, 'utf8')).toBe('untouched');
});
test('recovers atomic-write and publication hard links left by process death', () => {
const { staging, cacheRoot, cache } = fixture();
const archive = stage(staging, 'archive', 'crash-safe');
const entry = cache.promote(archive.path, archive.digest);
const metadata = path.join(cacheRoot, 'metadata', `${archive.digest}.json`);
fs.linkSync(metadata, `${metadata}.tmp.999.${'e'.repeat(8)}`);
expect(fs.statSync(metadata).nlink).toBe(2);
expect(cache.get(archive.digest)?.sha256).toBe(archive.digest);
expect(fs.statSync(metadata).nlink).toBe(1);
const incoming = path.join(cacheRoot, 'incoming', `.incoming-999-${'f'.repeat(24)}`);
fs.linkSync(entry.path, incoming);
fs.unlinkSync(metadata);
expect(fs.statSync(entry.path).nlink).toBe(2);
expect(cache.get(archive.digest)).toBeUndefined();
expect(fs.existsSync(incoming)).toBe(false);
expect(fs.existsSync(entry.path)).toBe(false);
});
test('a paused immutable lease publisher cannot be age-reclaimed or overlap a contender', () => {
const { cacheRoot, cache } = fixture(),lock=path.join(cacheRoot,'.lock'),leases=path.join(cacheRoot,'.mutation-lock-leases'),originalMkdir=fs.mkdirSync,originalUnlink=fs.unlinkSync;
let paused=false,contenderResult:string|undefined;
const contend=(published:string)=>{paused=true;const old=new Date(Date.now()-60_000);fs.utimesSync(published,old,old);contenderResult=code(()=>cache.stats());};
// The mkdir hook deterministically reproduces the former empty-directory
// publication. The unlink hook pauses the immutable replacement after its
// complete inode is visible and its temporary hard link is gone.
const mkdir=spyOn(fs,'mkdirSync').mockImplementation(((target:fs.PathLike,options?:fs.MakeDirectoryOptions & {recursive?:false})=>{const result=originalMkdir(target,options as any);if(!paused&&String(target)===lock)contend(lock);return result;}) as typeof fs.mkdirSync);
const unlink=spyOn(fs,'unlinkSync').mockImplementation(((target:fs.PathLike)=>{const value=String(target),result=originalUnlink(target);if(!paused&&value.startsWith(`${leases}${path.sep}`)&&/^[a-f0-9]{32}\.json\.tmp\.\d+\.[a-f0-9]{8}$/.test(path.basename(value)))contend(value.replace(/\.tmp\.\d+\.[a-f0-9]{8}$/,''));return result;}) as typeof fs.unlinkSync);
let result;try{result=cache.stats();}finally{unlink.mockRestore();mkdir.mockRestore();}
expect(paused).toBe(true);expect(contenderResult).toBe('INSUFFICIENT_CAPACITY');expect(result).toEqual({entries:0,bytes:0,maxBytes:1024});expect(fs.readdirSync(leases)).toEqual([]);expect(fs.lstatSync(lock).isFile()).toBe(true);
});
test('LRU eviction refuses poisoned links without following or deleting their targets', () => {
const { root, staging, cacheRoot, cache } = fixture(4);
const outside = path.join(root, 'outside');
fs.writeFileSync(outside, 'do-not-touch', { mode: 0o600 });
const digest = 'a'.repeat(64);
fs.symlinkSync(outside, path.join(cacheRoot, 'entries', digest));
fs.writeFileSync(path.join(cacheRoot, 'metadata', `${digest}.json`), JSON.stringify({ version: 1, sha256: digest, bytes: 12, createdAt: 1, lastAccessedAt: 1 }), { mode: 0o600 });
const fresh = stage(staging, 'fresh', '1234');
expect(code(() => cache.promote(fresh.path, fresh.digest))).toBe('UNSAFE_PATH');
expect(fs.readFileSync(outside, 'utf8')).toBe('do-not-touch');
});
test('fails closed on cache locks and poisoned incoming paths', () => {
const { staging, cacheRoot, cache } = fixture();
const archive = stage(staging, 'one', 'archive');
const lockTarget = path.join(cacheRoot, 'outside-lock');
fs.writeFileSync(lockTarget, 'lock-target');
fs.symlinkSync(lockTarget, path.join(cacheRoot, '.lock'));
expect(code(() => cache.promote(archive.path, archive.digest))).toBe('UNSAFE_PATH');
fs.unlinkSync(path.join(cacheRoot, '.lock'));
const outside = path.join(cacheRoot, 'outside-incoming');
fs.writeFileSync(outside, 'outside');
fs.symlinkSync(outside, path.join(cacheRoot, 'incoming', `.incoming-${process.pid}-${'b'.repeat(24)}`));
expect(code(() => cache.promote(archive.path, archive.digest))).toBe('UNSAFE_PATH');
expect(fs.readFileSync(outside, 'utf8')).toBe('outside');
});
});
+84
View File
@@ -0,0 +1,84 @@
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { canonical, sha256, type VerificationObservation } from '../lib/cso/contracts';
import { ISOLATION_POLICY_HASH } from '../lib/cso/docker';
import { saveReport, withLock } from '../lib/cso/state';
import { canonicalStartPlan, canonicalTestPlan, patchHash, verifyRepair } from '../lib/cso/verification';
const ROOT=path.resolve(import.meta.dir,'..'),launcher=path.join(ROOT,'bin',process.platform==='win32'?'gstack-cso-launcher.exe':'gstack-cso-launcher');
const tap=`TAP version 13
# Subtest: legitimate control remains available
ok 1 - legitimate control remains available
---
duration_ms: 1
...
1..1
# tests 1
# suites 0
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
`;
let root='',repo='',state='';
function command(args:string[]){return spawnSync(launcher,args,{cwd:repo,encoding:'utf8',env:{HOME:root,GSTACK_HOME:state,PATH:'/usr/bin:/bin'},timeout:30_000});}
function git(...args:string[]){const result=spawnSync('/usr/bin/git',['-C',repo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);return result.stdout;}
function runDir(run:any){return path.join(state,'security','cso',run.repoId,run.runId);}
function writeInput(name:string,value:unknown){const file=path.join(root,name);fs.writeFileSync(file,JSON.stringify(value));return file;}
beforeEach(()=>{
root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-cli-lifecycle-'));repo=path.join(root,'repo');state=path.join(root,'state');fs.mkdirSync(repo);
git('init','-q');git('config','user.email','fixture@example.test');git('config','user.name','Fixture');
fs.writeFileSync(path.join(repo,'package.json'),JSON.stringify({name:'cso-cli-lifecycle',version:'1.0.0',private:true,scripts:{start:'node app.js',test:'node --test'}})+'\n');
fs.writeFileSync(path.join(repo,'package-lock.json'),JSON.stringify({name:'cso-cli-lifecycle',version:'1.0.0',lockfileVersion:3,requires:true,packages:{'':{name:'cso-cli-lifecycle',version:'1.0.0'}}})+'\n');
fs.writeFileSync(path.join(repo,'app.js'),'module.exports = "vulnerable"\n');
fs.writeFileSync(path.join(repo,'app.test.js'),"const test=require('node:test');test('legitimate control remains available',()=>{});\n");
git('add','.');git('commit','-qm','fixture');
});
afterEach(()=>fs.rmSync(root,{recursive:true,force:true}));
describe('CSO interrupted-run and replay commands',()=>{
test('resume reports watchdog recovery without replenishing the original policy or deadline',()=>{
const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline','--budget','120']).stdout),dir=runDir(run),reportPath=path.join(dir,'report.json'),original=JSON.parse(fs.readFileSync(reportPath,'utf8'));
const recovery='supervisor-death execution-copy cleanup complete',dockerRecovery='deadline cleanup complete',control=path.join(dir,'supervision','repair-attempt'),preparationControl=path.join(dir,'preparation-execution','offline-attempt');fs.mkdirSync(control,{recursive:true});fs.mkdirSync(preparationControl,{recursive:true});fs.writeFileSync(path.join(control,'attempt.event'),`${recovery}\n`);fs.writeFileSync(path.join(preparationControl,'watchdog.event'),`${dockerRecovery}\n`);
original.status='interrupted';saveReport(dir,original);
const resumed=command(['resume',run.runId]);expect(resumed.status).toBe(0);const result=JSON.parse(resumed.stdout),active=JSON.parse(fs.readFileSync(reportPath,'utf8'));
expect(result).toMatchObject({runId:run.runId,deadline:original.deadline,policy:original.policy,recovery:[recovery,dockerRecovery]});expect(active.status).toBe('running');expect(active.deadline).toBe(original.deadline);expect(active.policy).toEqual(original.policy);
expect(active.events.filter((item:any)=>item.kind==='watchdog-recovery'&&[recovery,dockerRecovery].includes(item.message))).toHaveLength(2);expect(active.events.at(-1)).toMatchObject({kind:'resume',message:'Continued retained snapshot under original policy'});
active.status='interrupted';active.deadline=new Date(Date.now()-1_000).toISOString();const exhaustedDeadline=active.deadline;saveReport(dir,active);
const expired=command(['resume',run.runId]);expect(expired.status).not.toBe(0);expect(expired.stderr).toContain('DEADLINE');expect(expired.stderr).toContain('Original run budget is exhausted');
const retained=JSON.parse(fs.readFileSync(reportPath,'utf8'));expect(retained.status).toBe('interrupted');expect(retained.deadline).toBe(exhaustedDeadline);expect(retained.policy).toEqual(original.policy);expect(retained.events.at(-1)).toMatchObject({kind:'deadline',message:'Original budget is exhausted; resume did not replenish it'});
expect(retained.events.filter((item:any)=>item.kind==='watchdog-recovery'&&[recovery,dockerRecovery].includes(item.message))).toHaveLength(2);
});
test.skipIf(process.platform==='win32')('replay accepts retained or exactly matching supplied source and rejects expired or changed inputs before Docker admission',async()=>{
const started=command(['start','--repo',repo,'--comprehensive','--offline']);expect(started.status).toBe(0);const run=JSON.parse(started.stdout),dir=runDir(run),snapshot=path.join(dir,'snapshot'),manifest=JSON.parse(fs.readFileSync(path.join(dir,'snapshot.json'),'utf8'));
const finding={title:'Unprotected record read',rootCause:'Record lookup omits the caller authorization predicate',location:{path:'app.js',line:1,symbol:'recordLookup'},advisoryIds:[],severity:'high',confidence:'high',confidenceRationale:'The caller-to-record trace directly establishes the missing authorization predicate',evidence:'supported',attackerControl:'Authenticated caller chooses the record identifier',impact:'Another tenant record is returned',scenario:'A tenant supplies another tenant record identifier and receives the record',trace:['GET /records/:id','recordLookup','record response'],references:['app.js:1','OWASP API1:2023'],recommendation:'Bind the lookup to the authenticated caller before returning the record',challenge:{reviewer:'independent-reviewer',independent:true,mode:'independent_agent',callers:'Authenticated route forwards the record identifier',controls:'Authentication exists without record authorization',counterevidence:'Opaque identifiers reduce guessing but do not authorize known identifiers',conclusion:'The record lookup lacks a caller authorization predicate'}};
expect(command(['submit',run.runId,writeInput('replay-finding.json',{findings:[finding]})]).status).toBe(0);const reportPath=path.join(dir,'report.json'),report=JSON.parse(fs.readFileSync(reportPath,'utf8')),findingId=report.findings[0].id,start=canonicalStartPlan(snapshot,'node',3456),tests=canonicalTestPlan(snapshot,'node');
const request:any={findingId,runtimeProfile:`node-24.4.0-${process.arch==='arm64'?'arm64':'amd64'}`,port:3456,start:start.command,legitimate:[{name:'legitimate control',path:'/control',method:'GET',expected:{status:200,includes:'CONTROL_OK'}}],security:{name:'unauthorized record is denied',path:'/records/other',method:'GET',expected:{status:403,includes:'DENIED'},vulnerable:{status:200,includes:'RECORD'}},existingTests:tests.commands,fixtures:{},boundaryFiles:['app.js'],testFiles:tests.files,changes:[{path:'app.js',beforeSha256:sha256(fs.readFileSync(path.join(snapshot,'app.js'))),after:'module.exports = "fixed"\n',effect:'source'}],review:{reviewer:'independent-reviewer',independent:true,rootCauseRepaired:true,featurePreserved:true,boundaryMocks:false,rationale:'The caller predicate is added while the legitimate control and existing test remain unchanged.',reviewedPatchHash:''}};request.review.reviewedPatchHash=patchHash(request);
const platform=process.arch==='arm64'?'linux/arm64':'linux/amd64',runtime:any={id:request.runtimeProfile,stack:'node',platform,image:`ghcr.io/garrytan/gstack/cso-staging/node-${process.arch==='arm64'?'arm64':'amd64'}@sha256:${'b'.repeat(64)}`},executor={observe:async(_source:string,phase:'before'|'after',received:any,_runtime:any,_verifier:any,_work:string,_control:string,_execution:any,evidence:any,witness:any)=>{const observation:VerificationObservation={booted:true,legitimate:true,security:phase==='before'?'intended_failure':'pass',existingTests:false,output:`external ${phase} assertions passed`,inputHash:''},receipt=await witness.attest(observation,[{command:received.existingTests[0],code:0,output:tap,minimumPassingTests:evidence.minimumPassingTests[0]}]);return{observation:{...observation,existingTests:receipt.diagnosticTestsPassed,inputHash:witness.binding.harnessHash},witness:receipt};}};
const verified=await verifyRepair({runId:run.runId,runDir:dir,manifest,rawRequest:request,runtime,verifier:runtime,policyHash:ISOLATION_POLICY_HASH,auditPolicyHash:sha256(canonical(report.policy)),archives:[],executor});
report.findings[0].reproduction='reproduced';report.findings[0].repair='runtime_tested';report.findings[0].verificationId=verified.bundle.id;report.findings[0].verificationAssurance={assertions:'authenticated_out_of_process',testCompletion:'self_reported',review:'self_attested'};saveReport(dir,report);
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');
fs.writeFileSync(path.join(repo,'app.js'),'module.exports = "vulnerable"\n');const supplied=command(['replay',verified.bundle.id,'--source',repo]);expect(supplied.status).not.toBe(0);expect(supplied.stderr).toContain('PREREQUISITE');expect(supplied.stderr).toContain('MISSING_QUALIFIED_RUNTIME');expect(supplied.stderr).not.toContain('does not match the bundle input hashes');
},30_000);
});
+54
View File
@@ -0,0 +1,54 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawn, spawnSync } from 'node:child_process';
const ROOT=path.resolve(import.meta.dir,'..'),launcher=path.join(ROOT,'bin',process.platform==='win32'?'gstack-cso-launcher.exe':'gstack-cso-launcher');
let root='',repo='',state='';
const application={actors:['tenant user'],assets:['tenant records'],entrypoints:['GET /users/:id'],tenantBoundaries:['tenant id'],sensitiveOperations:['record read'],invariants:['tenant isolation']};
const finding={title:'Cross-tenant user read',rootCause:'Tenant query omits caller tenant predicate',location:{path:'src/users.ts',line:1,symbol:'tenantQuery'},advisoryIds:[],severity:'high',confidence:'high',confidenceRationale:'The caller-to-query trace and missing tenant predicate directly support the finding',evidence:'supported',attackerControl:'Authenticated caller chooses the record ID',impact:'Another tenant record is returned',scenario:'A tenant supplies a known record ID owned by another tenant and receives that record',trace:['GET /users/:id','tenantQuery','findUnique by id'],references:['src/users.ts:1','OWASP API1:2023'],recommendation:'Bind the lookup predicate to the authenticated tenant identifier',challenge:{reviewer:'independent-2',independent:true,mode:'independent_agent',callers:'Authenticated route forwards the ID',controls:'Authentication does not bind tenant',counterevidence:'Opaque IDs reduce guessing but do not authorize',conclusion:'The authorization invariant is absent'}};
function command(args:string[]){return spawnSync(launcher,args,{cwd:repo,encoding:'utf8',env:{HOME:root,GSTACK_HOME:state,PATH:'/usr/bin:/bin'},timeout:30_000});}
function git(...args:string[]){const result=spawnSync('/usr/bin/git',['-C',repo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);return result.stdout;}
function reportPath(run:any){return path.join(state,'security','cso',run.repoId,run.runId,'report.json');}
function completeEvidence(report:any,findings:any[]=[]){return{application,findings,coverage:report.coverage.filter((item:any)=>!['snapshot-inputs','history-inputs'].includes(item.domain)).map((item:any)=>({...item,status:'assessed',method:'fresh caller and boundary trace',gaps:[],evidence:['current captured source']})),gaps:[]};}
function writeInput(name:string,value:unknown){const file=path.join(root,name);fs.writeFileSync(file,JSON.stringify(value));return file;}
function recheckEvidence(source='src/users.ts'){return[
{kind:'caller',path:source,line:1,observation:'Fresh caller trace reaches the authorization predicate'},
{kind:'security_boundary',path:source,line:1,observation:'Fresh boundary trace confirms cross-tenant access is denied'},
];}
function finishedOriginal(name:string,candidate:any){
const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),initial=JSON.parse(fs.readFileSync(reportPath(run),'utf8'));
expect(command(['submit',run.runId,writeInput(`${name}.json`,completeEvidence(initial,[candidate]))]).status).toBe(0);
expect(command(['finish',run.runId]).status).toBe(0);
return{run,findingId:JSON.parse(fs.readFileSync(reportPath(run),'utf8')).findings[0].id};
}
beforeAll(()=>{root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-cli-recheck-'));repo=path.join(root,'repo');state=path.join(root,'state');fs.mkdirSync(repo);git('init','-q');git('config','user.email','fixture@example.test');git('config','user.name','Fixture');fs.mkdirSync(path.join(repo,'src'));fs.writeFileSync(path.join(repo,'src','users.ts'),'export const tenantQuery = (id:string) => db.user.findUnique({where:{id}})\n');fs.writeFileSync(path.join(repo,'src','other.ts'),'export const unrelated = true\n');git('add','src/users.ts','src/other.ts');git('commit','-qm','fixture');});
afterAll(()=>fs.rmSync(root,{recursive:true,force:true}));
describe('CSO recheck persistence',()=>{
test('resolves a unique finished original audit from the current repository',()=>{const candidate={...finding,rootCause:'Unique fixture query omits its tenant predicate',location:{...finding.location,symbol:'uniqueTenantQuery'}},original=finishedOriginal('unique-original',candidate),rechecked=command(['recheck',original.findingId,'--repo',repo]);expect(rechecked.status).toBe(0);expect(rechecked.stderr).toBe('');const child=JSON.parse(rechecked.stdout);expect(child.parent).toEqual({runId:original.run.runId,findingId:original.findingId,kind:'recheck'});},30_000);
test('reports a missing original finding without requiring an internal run ID',()=>{const missing=command(['recheck','0'.repeat(32),'--repo',repo]);expect(missing.status).not.toBe(0);expect(missing.stderr).toContain('MISSING_INPUT');expect(missing.stderr).toContain('No finished original audit contains this finding');},30_000);
test('rejects ambiguous originals and accepts --run as an explicit disambiguator',()=>{const candidate={...finding,rootCause:'Ambiguous fixture query omits its tenant predicate',location:{...finding.location,symbol:'ambiguousTenantQuery'}},first=finishedOriginal('ambiguous-original-first',candidate),second=finishedOriginal('ambiguous-original-second',candidate);expect(second.findingId).toBe(first.findingId);const ambiguous=command(['recheck',first.findingId,'--repo',repo]);expect(ambiguous.status).not.toBe(0);expect(ambiguous.stderr).toContain('INVALID_ARGUMENT');expect(ambiguous.stderr).toContain('--run RUN');const selected=command(['recheck',first.findingId,'--repo',repo,'--run',second.run.runId]);expect(selected.status).toBe(0);expect(JSON.parse(selected.stdout).parent.runId).toBe(second.run.runId);},30_000);
test('requires an immutable finished original audit',()=>{const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),report=JSON.parse(fs.readFileSync(reportPath(run),'utf8'));expect(command(['submit',run.runId,writeInput('unfinished.json',completeEvidence(report,[finding]))]).status).toBe(0);const id=JSON.parse(fs.readFileSync(reportPath(run),'utf8')).findings[0].id,recheck=command(['recheck',id,'--run',run.runId,'--repo',repo]);expect(recheck.status).not.toBe(0);expect(recheck.stderr).toContain('finished original audit');},30_000);
test('resolved closure rejects free-form or unrelated boundary evidence',()=>{const original=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),originalPath=reportPath(original),initial=JSON.parse(fs.readFileSync(originalPath,'utf8'));expect(command(['submit',original.runId,writeInput('bound-original.json',completeEvidence(initial,[finding]))]).status).toBe(0);expect(command(['finish',original.runId]).status).toBe(0);const findingId=JSON.parse(fs.readFileSync(originalPath,'utf8')).findings[0].id,started=JSON.parse(command(['recheck',findingId,'--run',original.runId,'--repo',repo]).stdout);
const freeForm=command(['submit',started.runId,writeInput('free-form-claim.json',{recheck:{findingId,outcome:'resolved',evidence:['caller words','boundary words'],rootCause:finding.rootCause}})]);expect(freeForm.status).not.toBe(0);expect(freeForm.stderr).toContain('recheck evidence[0]');
const unrelated=command(['submit',started.runId,writeInput('unrelated-boundary-claim.json',{recheck:{findingId,outcome:'resolved',evidence:[{kind:'caller',path:'src/users.ts',line:1,observation:'Fresh caller trace reaches the predicate'},{kind:'security_boundary',path:'src/other.ts',line:1,observation:'Unrelated source says nothing about authorization'}],rootCause:finding.rootCause}})]);expect(unrelated.status).not.toBe(0);expect(unrelated.stderr).toContain('original finding location');
const phantomLine=command(['submit',started.runId,writeInput('phantom-line-claim.json',{recheck:{findingId,outcome:'resolved',evidence:[{kind:'caller',path:'src/users.ts',line:2,observation:'A trailing newline must not create a source line'},{kind:'security_boundary',path:'src/users.ts',line:1,observation:'Fresh boundary trace checks authorization'}],rootCause:finding.rootCause}})]);expect(phantomLine.status).not.toBe(0);expect(phantomLine.stderr).toContain('line is outside the fresh source file');
},30_000);
test('a failed report write cannot leave a closure claim behind',()=>{const original=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),originalPath=reportPath(original),initial=JSON.parse(fs.readFileSync(originalPath,'utf8'));expect(command(['submit',original.runId,writeInput('original.json',completeEvidence(initial,[finding]))]).status).toBe(0);expect(command(['finish',original.runId]).status).toBe(0);const originalReport=JSON.parse(fs.readFileSync(originalPath,'utf8')),findingId=originalReport.findings[0].id,started=command(['recheck',findingId,'--run',original.runId,'--repo',repo]);expect(started.status).toBe(0);const child=JSON.parse(started.stdout),childPath=reportPath({...child,repoId:original.repoId}),childDir=path.dirname(childPath),childInitial=JSON.parse(fs.readFileSync(childPath,'utf8'));expect(command(['submit',child.runId,writeInput('child-baseline.json',completeEvidence(childInitial))]).status).toBe(0);const complete=JSON.parse(fs.readFileSync(childPath,'utf8'));expect(complete.completeness).toBe('complete');complete.events.push({at:new Date().toISOString(),kind:'padding',message:''});const empty=Buffer.byteLength(JSON.stringify(complete,null,2)+'\n'),target=1024*1024-8;complete.events.at(-1).message='x'.repeat(target-empty);const encoded=JSON.stringify(complete,null,2)+'\n';expect(Buffer.byteLength(encoded)).toBe(target);fs.writeFileSync(childPath,encoded);const failed=command(['submit',child.runId,writeInput('failed-claim.json',{modelUsage:{source:'host',tokens:0},recheck:{findingId,outcome:'resolved',evidence:recheckEvidence(),rootCause:finding.rootCause}})]);expect(failed.status).not.toBe(0);expect(failed.stderr).toContain('PERSISTENCE_FAILED');expect(fs.existsSync(path.join(childDir,'recheck-claim.json'))).toBe(false);},30_000);
test.skipIf(process.platform==='win32')('retention and recheck share the lease that protects an expiring parent',async()=>{const original=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),currentDir=path.dirname(reportPath(original)),initial=JSON.parse(fs.readFileSync(reportPath(original),'utf8'));expect(command(['submit',original.runId,writeInput('leased-original.json',completeEvidence(initial,[finding]))]).status).toBe(0);expect(command(['finish',original.runId]).status).toBe(0);const finished=JSON.parse(fs.readFileSync(reportPath(original),'utf8')),findingId=finished.findings[0].id,created=Date.now()-30*86400_000+60_000,oldRun=`${created}-${'8'.repeat(16)}`,oldDir=path.join(path.dirname(currentDir),oldRun);finished.runId=oldRun;fs.writeFileSync(path.join(currentDir,'report.json'),JSON.stringify(finished,null,2)+'\n');fs.renameSync(currentDir,oldDir);original.runId=oldRun;
const leases=path.join(oldDir,'.mutation-lock-leases'),active=/^[a-f0-9]{32}\.active\.[a-f0-9]{16}$/,ready=path.join(root,'lease-ready'),release=path.join(root,'lease-release'),holderSource=`import * as fs from 'node:fs';import {withLock} from ${JSON.stringify(path.join(ROOT,'lib/cso/state.ts'))};await withLock(${JSON.stringify(oldDir)},async()=>{fs.writeFileSync(${JSON.stringify(ready)},'ready',{flag:'wx'});while(!fs.existsSync(${JSON.stringify(release)}))await Bun.sleep(5);});`;
let holderStderr='';const holder=spawn(process.execPath,['--eval',holderSource],{env:{HOME:root,GSTACK_HOME:state,PATH:process.env.PATH},stdio:['ignore','ignore','pipe']}),holderClosed=new Promise<number|null>(resolve=>holder.on('close',resolve));holder.stderr!.setEncoding('utf8');holder.stderr!.on('data',chunk=>holderStderr+=chunk);let holderCode:number|null|'timeout'='timeout';
try{const readyOutcome=await Promise.race([(async()=>{const deadline=Date.now()+10_000;while(Date.now()<deadline){if(fs.existsSync(ready))return'ready' as const;await Bun.sleep(5);}return'timeout' as const;})(),holderClosed.then(code=>`closed:${code}` as const)]);expect(readyOutcome).toBe('ready');expect(fs.readdirSync(leases).some(name=>active.test(name))).toBe(true);const sweepNow=created+30*86400_000+1,sweep=spawnSync(process.execPath,['--eval',`import {retention} from ${JSON.stringify(path.join(ROOT,'lib/cso/state.ts'))};retention(${sweepNow});`],{encoding:'utf8',env:{HOME:root,GSTACK_HOME:state,PATH:process.env.PATH},timeout:30_000});expect(sweep.status).toBe(0);expect(fs.existsSync(path.join(oldDir,'report.json'))).toBe(true);const blocked=command(['recheck',findingId,'--run',oldRun,'--repo',repo]);expect(blocked.status).not.toBe(0);expect(blocked.stderr).toContain('INSUFFICIENT_CAPACITY');expect(blocked.stderr).toContain('Another helper is updating this run');}finally{fs.writeFileSync(release,'release');holderCode=await Promise.race([holderClosed,Bun.sleep(10_000).then(()=>'timeout' as const)]);if(holderCode==='timeout'){holder.kill('SIGKILL');holderCode=await holderClosed;}}
expect(holderCode).toBe(0);expect(holderStderr).toBe('');expect(fs.readdirSync(leases)).toEqual([]);const completed=command(['recheck',findingId,'--run',oldRun,'--repo',repo]);expect(completed.status).toBe(0);expect(completed.stderr).toBe('');const result=JSON.parse(completed.stdout);expect(fs.existsSync(path.join(oldDir,'report.json'))).toBe(true);expect(fs.existsSync(path.join(state,'security','cso',original.repoId,result.runId,'report.json'))).toBe(true);
},30_000);
test('closure cannot survive mutation of the fresh recheck snapshot',()=>{const original=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),originalPath=reportPath(original),initial=JSON.parse(fs.readFileSync(originalPath,'utf8'));expect(command(['submit',original.runId,writeInput('source-bound-original.json',completeEvidence(initial,[finding]))]).status).toBe(0);expect(command(['finish',original.runId]).status).toBe(0);const originalReport=JSON.parse(fs.readFileSync(originalPath,'utf8')),findingId=originalReport.findings[0].id,started=command(['recheck',findingId,'--run',original.runId,'--repo',repo]);expect(started.status).toBe(0);const child=JSON.parse(started.stdout),childPath=reportPath({...child,repoId:original.repoId}),childDir=path.dirname(childPath),childInitial=JSON.parse(fs.readFileSync(childPath,'utf8')),claim={findingId,outcome:'resolved',evidence:recheckEvidence(),rootCause:finding.rootCause};expect(command(['submit',child.runId,writeInput('source-bound-claim.json',{...completeEvidence(childInitial),recheck:claim})]).status).toBe(0);fs.writeFileSync(path.join(childDir,'snapshot','src','users.ts'),'mutated retained evidence\n');const finished=command(['finish',child.runId]);expect(finished.status).not.toBe(0);expect(finished.stderr).toContain('Retained snapshot changed');expect(JSON.parse(fs.readFileSync(originalPath,'utf8')).findings[0].closure).toBe('open');},30_000);
test('complete fresh-source evidence resolves only the targeted original finding',()=>{const second={...finding,title:'Unprotected audit export',rootCause:'Audit export omits the caller authorization predicate',location:{...finding.location,symbol:'auditExport'},trace:['GET /audit/export','auditExport','record response'],references:['src/users.ts:1'],scenario:'A tenant requests another tenant audit export and receives it'},original=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),originalPath=reportPath(original),initial=JSON.parse(fs.readFileSync(originalPath,'utf8'));expect(command(['submit',original.runId,writeInput('closure-original.json',completeEvidence(initial,[finding,second]))]).status).toBe(0);expect(command(['finish',original.runId]).status).toBe(0);const finishedOriginal=JSON.parse(fs.readFileSync(originalPath,'utf8')),findingId=finishedOriginal.findings.find((item:any)=>item.rootCause===finding.rootCause).id,otherId=finishedOriginal.findings.find((item:any)=>item.rootCause===second.rootCause).id,originalHash=finishedOriginal.source.originalHash,source=path.join(repo,'src','users.ts'),before=fs.readFileSync(source,'utf8');
try{
fs.writeFileSync(source,'export const tenantQuery = (tenantId:string,id:string) => db.user.findUnique({where:{id,tenantId}})\n');const started=command(['recheck',findingId,'--run',original.runId,'--repo',repo]);expect(started.status).toBe(0);const child=JSON.parse(started.stdout),childPath=reportPath({...child,repoId:original.repoId}),childInitial=JSON.parse(fs.readFileSync(childPath,'utf8')),claim={findingId,outcome:'resolved',evidence:recheckEvidence(),rootCause:finding.rootCause};expect(childInitial.source.originalHash).not.toBe(originalHash);expect(JSON.parse(fs.readFileSync(originalPath,'utf8')).findings.every((item:any)=>item.closure==='open')).toBe(true);
expect(command(['submit',child.runId,writeInput('closure-claim-first.json',{recheck:claim})]).status).toBe(0);expect(JSON.parse(fs.readFileSync(originalPath,'utf8')).findings.find((item:any)=>item.id===findingId).closure).toBe('open');const incomplete=command(['finish',child.runId]);expect(incomplete.status).not.toBe(0);expect(incomplete.stderr).toContain('Partial or incompatible rechecks cannot establish closure');expect(JSON.parse(fs.readFileSync(originalPath,'utf8')).findings.find((item:any)=>item.id===findingId).closure).toBe('open');
expect(command(['submit',child.runId,writeInput('closure-fresh-evidence.json',completeEvidence(childInitial))]).status).toBe(0);const closed=command(['finish',child.runId]);expect(closed.status).toBe(0);expect(JSON.parse(closed.stdout)).toMatchObject({status:'finished',completeness:'complete'});const updated=JSON.parse(fs.readFileSync(originalPath,'utf8'));expect(updated.findings.find((item:any)=>item.id===findingId).closure).toBe('resolved');expect(updated.findings.find((item:any)=>item.id===otherId).closure).toBe('open');expect(updated.events.at(-1)).toMatchObject({kind:'closure'});expect(updated.events.at(-1).message).toContain(`resolved ${findingId}`);
}finally{fs.writeFileSync(source,before);}
},30_000);
});
+118
View File
@@ -0,0 +1,118 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';import * as os from 'node:os';import * as path from 'node:path';import { spawnSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { fingerprint, sha256 } from '../lib/cso/contracts';
import { patchHash } from '../lib/cso/verification';
import { redact } from '../lib/cso/process';
const ROOT=path.resolve(import.meta.dir,'..'),launcher=path.join(ROOT,'bin',process.platform==='win32'?'gstack-cso-launcher.exe':'gstack-cso-launcher');let root='',repo='',state='';
const CREDENTIAL_CANARY=['ghp_','abcdefghijklmnopqrstuvwxyz1234567890'].join('');
function command(args:string[],extra:Record<string,string>={},file=launcher){const r=spawnSync(file,args,{cwd:repo,encoding:'utf8',env:{HOME:root,GSTACK_HOME:state,PATH:`${repo}/hostile-bin:/usr/bin:/bin`,BUN_OPTIONS:`--preload=${repo}/preload.ts`,BUN_BE_BUN:'1',NODE_OPTIONS:`--require=${repo}/preload.cjs`,RUBYOPT:`-r${repo}/preload.rb`,PYTHONPATH:repo,LD_PRELOAD:path.join(repo,'missing.so'),BASH_ENV:path.join(repo,'bashenv'),...extra},timeout:30_000});return r;}
function git(...args:string[]){const r=spawnSync('/usr/bin/git',['-C',repo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(r.status)throw new Error(r.stderr);return r.stdout;}
beforeAll(()=>{root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-cli-'));repo=path.join(root,'repo');state=path.join(root,'state');fs.mkdirSync(repo);git('init','-q');git('config','user.email','fixture@example.test');git('config','user.name','Fixture');fs.mkdirSync(path.join(repo,'src'));fs.writeFileSync(path.join(repo,'src','users.ts'),'export const tenantQuery = (id:string) => db.user.findUnique({where:{id}})\n');fs.writeFileSync(path.join(repo,'src','control.test.ts'),'export const control = true\n');fs.writeFileSync(path.join(repo,'.env'),`TOKEN=${CREDENTIAL_CANARY}\n`);git('add','src/users.ts','src/control.test.ts','.env');git('commit','-qm','fixture');fs.mkdirSync(path.join(repo,'hostile-bin'));for(const tool of ['git','dirname'])fs.writeFileSync(path.join(repo,'hostile-bin',tool),`#!/bin/sh\ntouch '${path.join(repo,'path-shim-ran')}'\nexit 99\n`,{mode:0o755});for(const f of ['preload.ts','preload.cjs','preload.rb','bashenv'])fs.writeFileSync(path.join(repo,f),`require('fs').writeFileSync('${path.join(repo,'autoload-ran')}','yes')\n`);});
afterAll(()=>fs.rmSync(root,{recursive:true,force:true}));
describe('compiled CSO command workflow',()=>{
test('trusted launcher resists runtime autoload variables and repository PATH shims',()=>{const r=command(['--version']);expect(r.status).toBe(0);expect(JSON.parse(r.stdout)).toEqual({version:'3.0.0',abi:3});expect(fs.existsSync(path.join(repo,'autoload-ran'))).toBe(false);expect(fs.existsSync(path.join(repo,'path-shim-ran'))).toBe(false);});
test('trusted launcher resolves relative caller paths without trusting an inherited cwd override',()=>{const hostile=path.join(root,'missing-caller-cwd'),diagnosed=command(['doctor','--repo','.'],{GSTACK_CSO_CALLER_CWD:hostile,DOCKER_HOST:'tcp://127.0.0.1:2375'});expect(diagnosed.status).toBe(0);expect(JSON.parse(diagnosed.stdout).checks.find((item:any)=>item.capability==='static-snapshot')).toMatchObject({status:'ready'});const started=command(['start','--repo','.','--scope','auth','--offline'],{GSTACK_CSO_CALLER_CWD:hostile});expect(started.status).toBe(0);const run=JSON.parse(started.stdout),input=path.join(repo,'relative-submission.json');try{fs.writeFileSync(input,'{}');expect(command(['submit',run.runId,'relative-submission.json'],{GSTACK_CSO_CALLER_CWD:hostile}).status).toBe(0);}finally{fs.rmSync(input,{force:true});}});
test.skipIf(process.platform!=='linux')('static launcher prevents loader injection before environment scrubbing',()=>{const source=path.join(root,'preload.c'),library=path.join(root,'preload.so'),marker=path.join(root,'loader-ran');fs.writeFileSync(source,'#include <fcntl.h>\n#include <stdlib.h>\n#include <unistd.h>\n__attribute__((constructor)) static void mark(void){const char*p=getenv("CSO_PRELOAD_MARKER");if(p){int f=open(p,O_WRONLY|O_CREAT,0600);if(f>=0)close(f);}}\n');const built=spawnSync('/usr/bin/cc',['-shared','-fPIC',source,'-o',library],{encoding:'utf8',timeout:30_000});expect(built.status).toBe(0);const r=command(['--version'],{LD_PRELOAD:library,CSO_PRELOAD_MARKER:marker});expect(r.status).toBe(0);expect(fs.existsSync(marker)).toBe(false);});
test('package-manager launcher symlinks resolve the trusted sibling core',()=>{const npmBin=path.join(root,'npm-bin');fs.mkdirSync(npmBin,{recursive:true});const link=path.join(npmBin,'gstack-cso');fs.symlinkSync(launcher,link);const r=command(['--version'],{},link);expect(r.status).toBe(0);expect(JSON.parse(r.stdout)).toEqual({version:'3.0.0',abi:3});});
test.skipIf(process.platform==='win32')('compiled core starts in the canonical trusted launcher directory',()=>{const install=path.join(root,'trusted launcher'),hostile=path.join(repo,'hostile cwd');fs.mkdirSync(install);fs.mkdirSync(hostile);const publicLauncher=path.join(install,'gstack-cso-launcher'),fixtureCore=path.join(install,'gstack-cso-core'),source=path.join(root,'cwd-fixture.c');fs.writeFileSync(source,'#include <stdio.h>\n#include <unistd.h>\n#include <limits.h>\nint main(void){char cwd[PATH_MAX];if(!getcwd(cwd,sizeof cwd))return 2;puts(cwd);return 0;}\n');const built=spawnSync('/usr/bin/cc',[source,'-o',fixtureCore],{encoding:'utf8',timeout:30_000});expect(built.status).toBe(0);const digest=createHash('sha256').update(fs.readFileSync(fixtureCore)).digest('hex'),launcherBuild=spawnSync('/usr/bin/cc',['-std=c11','-D_POSIX_C_SOURCE=200809L','-O2',`-DGSTACK_CSO_CORE_SHA256=\"${digest}\"`,path.join(ROOT,'lib/cso/launcher.c'),'-o',publicLauncher],{encoding:'utf8',timeout:30_000});expect(launcherBuild.status).toBe(0);fs.writeFileSync(path.join(install,'.gstack-cso-generation'),`${digest}\n`);const r=spawnSync(publicLauncher,[],{cwd:hostile,encoding:'utf8',env:{...process.env,HOME:root,GSTACK_HOME:state},timeout:30_000});expect(r.status).toBe(0);expect(r.stdout.trim()).toBe(fs.realpathSync(install));expect(r.stdout.trim()).not.toBe(fs.realpathSync(hostile));});
test('schema advertises only caller-owned verification input',()=>{const r=command(['schema']);expect(r.status).toBe(0);const schema=JSON.parse(r.stdout);expect(schema.verification).not.toHaveProperty('result');expect(schema.helperOwned).toContain('repair result and label');});
test('v2 imports remain readable and immutable without upgrading legacy proof',()=>{
const source=path.join(root,'legacy-v2.json'),legacy={schemaVersion:2,findings:[{status:'VERIFIED',title:'Historical authorization finding',description:`Captured credential ${CREDENTIAL_CANARY}`,fingerprint:'legacy-fingerprint',location:{path:'src/legacy.ts',line:17,symbol:'legacyAuthorization'},impact:'Cross-tenant disclosure',recommendation:'Bind the lookup to the authenticated tenant',repair:'VERIFIED'}]};
fs.writeFileSync(source,JSON.stringify(legacy));
const imported=command(['import-v2',source]);expect(imported.status).toBe(0);expect(imported.stdout+imported.stderr).not.toContain(CREDENTIAL_CANARY);const result=JSON.parse(imported.stdout),finding=result.report.findings[0];
expect(result.id).toMatch(/^[a-f0-9]{64}$/);expect(result.warning).toContain('does not establish reproduction, tested repair, or closure');expect(result.report).toMatchObject({schemaVersion:2,readOnly:true});
expect(finding).toMatchObject({status:'VERIFIED',evidence:'legacy_review',reproduction:'not_attempted',repair:'not_attempted',closure:'unknown',legacy:{fingerprint:'legacy-fingerprint',location:{path:'src/legacy.ts',line:17,symbol:'legacyAuthorization'},impact:'Cross-tenant disclosure',recommendation:'Bind the lookup to the authenticated tenant',repair:'VERIFIED'}});
fs.writeFileSync(source,JSON.stringify({schemaVersion:2,findings:[]}));
const inspected=command(['inspect-v2',result.id]);expect(inspected.status).toBe(0);expect(inspected.stdout+inspected.stderr).not.toContain(CREDENTIAL_CANARY);expect(JSON.parse(inspected.stdout)).toEqual({id:result.id,report:result.report});
const stored=path.join(state,'security','cso','legacy-imports',`${result.id}.json`);expect(fs.existsSync(stored)).toBe(true);expect(fs.readFileSync(stored,'utf8')).not.toContain(CREDENTIAL_CANARY);
const invalid=command(['inspect-v2','../legacy-v2.json']);expect(invalid.status).not.toBe(0);expect(invalid.stderr).toContain('64-character import ID');
const missing=command(['inspect-v2','0'.repeat(64)]);expect(missing.status).not.toBe(0);expect(missing.stderr).toContain('MISSING_INPUT');
const beforeUnsafe=fs.readdirSync(path.dirname(stored)).length,unsafeKey=`severity\u202eHIGH`;fs.writeFileSync(source,JSON.stringify({schemaVersion:2,findings:[{status:'OPEN',title:'unsafe',nested:{[unsafeKey]:'forged'}}]}));const unsafe=command(['import-v2',source]);expect(unsafe.status).not.toBe(0);expect(unsafe.stderr).toContain('unsafe property');expect(unsafe.stdout+unsafe.stderr).not.toContain(unsafeKey);expect(fs.readdirSync(path.dirname(stored))).toHaveLength(beforeUnsafe);
const changed=JSON.parse(fs.readFileSync(stored,'utf8'));changed.findings[0].status='CHANGED';fs.writeFileSync(stored,JSON.stringify(changed,null,2)+'\n');const tampered=command(['inspect-v2',result.id]);expect(tampered.status).not.toBe(0);expect(tampered.stderr).toContain('identity is inconsistent');
});
test('wallet-shaped source paths remain addressable through stable opaque handles',()=>{
const walletRepo=path.join(root,'wallet-path-repo'),wallet='0x1234567890abcdef1234567890abcdef12345678',sourcePath=`src/${wallet}.ts`;
fs.mkdirSync(path.join(walletRepo,'src'),{recursive:true});
const runGit=(...args:string[])=>{const result=spawnSync('/usr/bin/git',['-C',walletRepo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);};
runGit('init','-q');runGit('config','user.email','fixture@example.test');runGit('config','user.name','Fixture');
fs.writeFileSync(path.join(walletRepo,sourcePath),`export const value = true\n// TOKEN=${CREDENTIAL_CANARY}\n`);fs.writeFileSync(path.join(walletRepo,'control.test.js'),'test(\"control\",()=>{})\n');runGit('add','.');runGit('commit','-qm','wallet-shaped path');
const started=command(['start','--repo',walletRepo,'--comprehensive','--offline']);expect(started.status).toBe(0);
const run=JSON.parse(started.stdout),dir=path.join(state,'security','cso',run.repoId,run.runId),privateManifest=JSON.parse(fs.readFileSync(path.join(dir,'snapshot.json'),'utf8'));
const privateEntry=privateManifest.entries.find((item:any)=>item.path===sourcePath);expect(privateEntry.path).toBe(sourcePath);expect(privateEntry.pathId).toMatch(/^[a-f0-9]{32}$/);
const inspected=command(['inspect',run.runId]);expect(inspected.status).toBe(0);const view=JSON.parse(inspected.stdout),entry=view.manifest.entries.find((item:any)=>item.displayPath);
expect(JSON.stringify(view)).not.toContain(wallet);expect(JSON.stringify(view)).not.toContain('ghp_');expect(entry.path).toMatch(/^@cso-path\/\/[a-f0-9]{32}$/);expect(entry.displayPath).toContain('<REDACTED-pii.wallet>');expect(entry).not.toHaveProperty('pathId');expect(view.sensitiveEvidence.find((item:any)=>item.path===entry.path)).toBeDefined();
const read=command(['read',run.runId,entry.path]);expect(read.status).toBe(0);expect(read.stdout).toContain('export const value = true');
const history=command(['history',run.runId,entry.path]);expect(history.status).toBe(0);expect(history.stdout).toContain('<REDACTED-pii.wallet>');expect(history.stdout).toContain('export const value = true');expect(history.stdout).not.toContain('No retained patch hunks');expect(history.stdout).not.toContain(wallet);
const input=path.join(root,`wallet-path-${run.runId}.json`),finding={title:'Sensitive-shaped source path',rootCause:'Public route omits an authorization predicate',location:{path:entry.path,line:1,symbol:'value'},advisoryIds:[],severity:'high',confidence:'high',confidenceRationale:'A caller-to-sink trace establishes the missing predicate',evidence:'supported',attackerControl:'Caller chooses the identifier',impact:'Unauthorized record disclosure',scenario:'A caller requests another tenant record',trace:['route','value','response'],references:[`${entry.displayPath}:1`],recommendation:'Bind access to the authenticated tenant',challenge:{reviewer:'reviewer-session',independent:true,mode:'independent_agent',callers:'route caller checked',controls:'authorization is absent',counterevidence:'none found',conclusion:'the tenant predicate is absent'}};
fs.writeFileSync(input,JSON.stringify({findings:[finding]}));const submitted=command(['submit',run.runId,input]);expect(submitted.status).toBe(0);
const report=JSON.parse(fs.readFileSync(path.join(dir,'report.json'),'utf8'));expect(report.findings).toHaveLength(1);expect(report.findings[0].location.path).toBe(entry.path);expect(report.findings[0].id).toBe(fingerprint(report.findings[0]));expect(JSON.stringify(report)).not.toContain(wallet);
const before=fs.readFileSync(path.join(dir,'snapshot',sourcePath),'utf8'),request:any={findingId:report.findings[0].id,runtimeProfile:'node-v1',port:3456,start:{executable:'/usr/local/bin/node',args:['app.js']},legitimate:[{name:'control',path:'/health',method:'GET',expected:{status:200}}],security:{name:'authorization',path:'/records/other',method:'GET',expected:{status:403},vulnerable:{status:200}},existingTests:[{executable:'/usr/local/bin/node',args:['--test']}],testFiles:['control.test.js'],fixtures:{},boundaryFiles:[entry.path],changes:[{path:entry.path,beforeSha256:sha256(before),after:'export const value = false\n',effect:'source'}],review:{reviewer:'reviewer-session',independent:true,rootCauseRepaired:true,featurePreserved:true,boundaryMocks:false,rationale:'The authorization decision replaces the vulnerable behavior while preserving the control',reviewedPatchHash:''}};request.review.reviewedPatchHash=patchHash(request);fs.writeFileSync(input,JSON.stringify(request));const reviewed=command(['record-review',run.runId,input,'--producer','producer-session']);expect(reviewed.status).toBe(0);request.review.artifactId=JSON.parse(reviewed.stdout).reviewArtifactId;fs.writeFileSync(input,JSON.stringify(request));const verified=command(['verify',run.runId,input]);expect(verified.status).not.toBe(0);expect(verified.stderr).toContain('PREREQUISITE');expect(verified.stderr).not.toContain('Boundary files');expect(verified.stderr).not.toContain('contained relative path');
expect(report.coverage.find((item:any)=>item.domain==='snapshot-inputs')).toMatchObject({status:'assessed',gaps:[]});
},15_000);
test('daily audits expose staged tracked deletions as replay-stable handles and material gaps',()=>{
const deletedRepo=path.join(root,'deleted-path-repo'),wallet='0xabcdefabcdefabcdefabcdefabcdefabcdefabcd',sourcePath=`src/${wallet}.ts`;
fs.mkdirSync(path.join(deletedRepo,'src'),{recursive:true});
const runGit=(...args:string[])=>{const result=spawnSync('/usr/bin/git',['-C',deletedRepo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);return result.stdout;};
runGit('init','-q');runGit('config','user.email','fixture@example.test');runGit('config','user.name','Fixture');fs.writeFileSync(path.join(deletedRepo,sourcePath),'export const authorization = true\n');fs.writeFileSync(path.join(deletedRepo,'app.js'),'export const app = true\n');runGit('add','.');runGit('commit','-qm','tracked authorization source');runGit('rm','-q',sourcePath);
const started=command(['start','--repo',deletedRepo,'--scope','auth','--offline']);expect(started.status).toBe(0);const run=JSON.parse(started.stdout),dir=path.join(state,'security','cso',run.repoId,run.runId),privateManifest=JSON.parse(fs.readFileSync(path.join(dir,'snapshot.json'),'utf8'));
expect(privateManifest.entries.some((item:any)=>item.path===sourcePath)).toBe(false);expect(privateManifest.deletedPaths).toHaveLength(1);expect(privateManifest.deletedPaths[0]).toMatchObject({path:sourcePath});expect(privateManifest.deletedPaths[0].pathId).toMatch(/^[a-f0-9]{32}$/);
const inspected=command(['inspect',run.runId]);expect(inspected.status).toBe(0);const view=JSON.parse(inspected.stdout),deleted=view.manifest.deletedPaths[0],coverage=view.report.coverage.find((item:any)=>item.domain==='snapshot-inputs');
expect(JSON.stringify(view)).not.toContain(wallet);expect(deleted.path).toMatch(/^@cso-path\/\/[a-f0-9]{32}$/);expect(deleted.displayPath).toContain('<REDACTED-pii.wallet>');expect(deleted).not.toHaveProperty('pathId');expect(coverage.status).toBe('partial');expect(coverage.gaps).toEqual([`${deleted.path}: tracked source is deleted from the worktree; only retained history is available for assessment`]);expect(view.report.source.transformations).toContainEqual({path:deleted.path,handling:'tracked source deleted; retained history only'});
const history=command(['history',run.runId,deleted.path]);expect(history.status).toBe(0);expect(history.stdout).toContain('<REDACTED-pii.wallet>');expect(history.stdout).not.toContain(wallet);const read=command(['read',run.runId,deleted.path]);expect(read.status).not.toBe(0);expect(read.stderr).toContain('outside the retained inventory');
const tampered=structuredClone(privateManifest);delete tampered.deletedPaths;fs.writeFileSync(path.join(dir,'snapshot.json'),JSON.stringify(tampered,null,2)+'\n');const rejected=command(['inspect',run.runId]);expect(rejected.status).not.toBe(0);expect(rejected.stderr).toContain('Snapshot original identity is inconsistent');
});
test('runtime plans encode sensitive canonical test argv with snapshot handles',()=>{
const planRepo=path.join(root,'sensitive-plan-repo'),wallet='0x1111111111111111111111111111111111111112',testPath=`${wallet}.test.js`;fs.mkdirSync(planRepo);
const runGit=(...args:string[])=>{const result=spawnSync('/usr/bin/git',['-C',planRepo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);};
runGit('init','-q');runGit('config','user.email','fixture@example.test');runGit('config','user.name','Fixture');fs.writeFileSync(path.join(planRepo,'package.json'),JSON.stringify({name:'sensitive-plan',version:'1.0.0',scripts:{start:'node app.js',test:'node --test'}}));fs.writeFileSync(path.join(planRepo,'package-lock.json'),JSON.stringify({name:'sensitive-plan',version:'1.0.0',lockfileVersion:3,packages:{'':{name:'sensitive-plan',version:'1.0.0'}}}));fs.writeFileSync(path.join(planRepo,'app.js'),'export const app = true\n');fs.writeFileSync(path.join(planRepo,testPath),'const test=require("node:test");test("control",()=>{})\n');runGit('add','.');runGit('commit','-qm','sensitive test path');
const started=command(['start','--repo',planRepo,'--comprehensive','--offline']);expect(started.status).toBe(0);const run=JSON.parse(started.stdout),planned=command(['runtime-plan',run.runId,'node','--port','3456']);expect(planned.status).toBe(0);const plan=JSON.parse(planned.stdout),serialized=JSON.stringify(plan);
expect(serialized).not.toContain(wallet);const testHandle=plan.tests.files.find((value:string)=>value.startsWith('@cso-path//'));expect(testHandle).toMatch(/^@cso-path\/\/[a-f0-9]{32}$/);expect(plan.tests.commands[0].args).toContain(`./${testHandle}`);expect(plan.start.command).toEqual({executable:'/usr/local/bin/node',args:['app.js']});
});
test('wallet-shaped helper finding IDs survive submit, review, and verification admission',()=>{const started=command(['start','--repo',repo,'--comprehensive','--offline']);expect(started.status).toBe(0);const run=JSON.parse(started.stdout),base:any={title:'Wallet-shaped helper ID',rootCause:'Tenant query omits caller tenant predicate 0',location:{path:'src/users.ts',line:1,symbol:'tenantQuery'},advisoryIds:[],severity:'high',confidence:'high',confidenceRationale:'Caller-to-sink trace directly establishes the missing predicate',evidence:'supported',attackerControl:'Tenant chooses the record ID',impact:'Cross-tenant record disclosure',scenario:'A tenant requests another tenant record by ID',trace:['route','tenantQuery','database'],references:['src/users.ts:1'],recommendation:'Bind the query to the authenticated tenant',challenge:{reviewer:'reviewer-session',independent:true,mode:'independent_agent',callers:'route caller checked',controls:'authentication lacks authorization',counterevidence:'none found',conclusion:'tenant predicate is absent'}};let helperId='';for(let i=0;i<10_000;i++){base.rootCause=`Tenant query omits caller tenant predicate ${i}`;helperId=fingerprint(base);if(redact(helperId)!==helperId)break;}expect(redact(helperId)).not.toBe(helperId);const input=path.join(root,`wallet-id-${run.runId}.json`);fs.writeFileSync(input,JSON.stringify({findings:[base]}));expect(command(['submit',run.runId,input]).status).toBe(0);const reportPath=path.join(state,'security','cso',run.repoId,run.runId,'report.json'),stored=JSON.parse(fs.readFileSync(reportPath,'utf8'));expect(stored.findings[0].id).toBe(helperId);const before=fs.readFileSync(path.join(repo,'src','users.ts'),'utf8'),request:any={findingId:helperId,runtimeProfile:'node-v1',port:3456,start:{executable:'/usr/local/bin/node',args:['src/users.ts']},legitimate:[{name:'control',path:'/health',method:'GET',expected:{status:200}}],security:{name:'tenant boundary',path:'/users/other',method:'GET',expected:{status:403},vulnerable:{status:200}},existingTests:[{executable:'/usr/local/bin/node',args:['--test']}],testFiles:['src/control.test.ts'],fixtures:{},boundaryFiles:['src/users.ts'],changes:[{path:'src/users.ts',beforeSha256:sha256(before),after:before+'// tenant predicate\n',effect:'source'}],review:{reviewer:'reviewer-session',independent:true,rootCauseRepaired:true,featurePreserved:true,boundaryMocks:false,rationale:'The tenant predicate is added while the control remains intact',reviewedPatchHash:''}};request.review.reviewedPatchHash=patchHash(request);fs.writeFileSync(input,JSON.stringify(request));const reviewed=command(['record-review',run.runId,input,'--producer','producer-session']);expect(reviewed.status).toBe(0);request.review.artifactId=JSON.parse(reviewed.stdout).reviewArtifactId;fs.writeFileSync(input,JSON.stringify(request));const verified=command(['verify',run.runId,input]);expect(verified.status).not.toBe(0);expect(verified.stderr).toContain('PREREQUISITE');expect(verified.stderr).not.toContain('REDACTION_FAILED');const review=JSON.parse(fs.readFileSync(path.join(path.dirname(reportPath),'reviews',`${request.review.artifactId}.json`),'utf8'));expect(review.findingId).toBe(helperId);});
test('daily find, early submit, and finish persist a truthful report without changing source',()=>{const before=git('status','--porcelain=v1','-z'),started=command(['start','--repo',repo,'--scope','auth','--offline']);expect(started.status).toBe(0);const run=JSON.parse(started.stdout),runId=run.runId;expect(run.completeness).toBe('not assessed');const reportPath=path.join(state,'security','cso',run.repoId,runId,'report.json');expect(fs.existsSync(reportPath)).toBe(true);const current=JSON.parse(fs.readFileSync(reportPath,'utf8'));
const wallet='0x1234567890abcdef1234567890abcdef12345678',submission={application:{actors:['authenticated tenant user'],assets:['tenant records'],entrypoints:['GET /users/:id'],tenantBoundaries:['record.tenant_id equals session.tenant_id'],sensitiveOperations:['read tenant record'],invariants:['a tenant cannot read another tenant record']},findings:[{title:'Cross-tenant user read',rootCause:`Tenant query ${wallet} omits caller tenant predicate`,location:{path:'src/users.ts',line:1,symbol:'tenantQuery'},advisoryIds:[],severity:'high',confidence:'high',confidenceRationale:'The captured caller-to-sink trace and control review directly support the finding',evidence:'supported',attackerControl:'Authenticated caller chooses the record ID',impact:'Another tenant record is returned',scenario:'A tenant supplies a known record ID owned by another tenant and receives that record',trace:['GET /users/:id','tenantQuery','findUnique by id'],references:['src/users.ts:1','OWASP API1:2023'],recommendation:'Bind the lookup predicate to the authenticated tenant identifier before returning the record',challenge:{reviewer:'independent-2',independent:true,mode:'independent_agent',callers:'Authenticated route forwards path ID',controls:'Authentication exists; authorization predicate does not',counterevidence:'Opaque IDs reduce guessing but do not authorize',conclusion:'The caller can provide a known cross-tenant ID'}}],coverage:current.coverage.filter((c:any)=>!['snapshot-inputs','history-inputs'].includes(c.domain)).map((c:any)=>({...c,status:'assessed',method:'caller and middleware trace',gaps:[],evidence:[`${c.domain} inspected against captured source`]})),gaps:[]};const file=path.join(root,'submission.json');fs.writeFileSync(file,JSON.stringify(submission));const submitted=command(['submit',runId,file]);expect(submitted.status).toBe(0);expect(JSON.parse(fs.readFileSync(reportPath,'utf8')).findings).toHaveLength(1);const finished=command(['finish',runId]);expect(finished.status).toBe(0);const final=JSON.parse(fs.readFileSync(reportPath,'utf8'));expect(final.completeness).toBe('complete');expect(final.findings[0]).toMatchObject({reproduction:'not_attempted',repair:'not_attempted',closure:'open'});expect(final.findings[0].id).toBe(fingerprint(final.findings[0]));expect(JSON.stringify(final)).not.toContain(wallet);const markdown=fs.readFileSync(path.join(path.dirname(reportPath),'report.md'),'utf8');expect(markdown.startsWith('complete — domain:auth')).toBe(true);expect(markdown).toContain('.env: excluded: credential or execution configuration');expect(markdown).not.toContain('ghp_');expect(git('status','--porcelain=v1','-z')).toBe(before);});
test('credential originals are withheld rather than printed',()=>{const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout);const result=command(['read',run.runId,'.env']);expect(result.status).not.toBe(0);expect(result.stderr).toContain('MISSING_INPUT');expect(result.stdout+result.stderr).not.toContain('ghp_');});
test('bounded submission and snapshot readers reject multiply-linked pathnames',()=>{const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),dir=path.join(state,'security','cso',run.repoId,run.runId),inputSource=path.join(root,`linked-input-source-${run.runId}.json`),input=path.join(root,`linked-input-${run.runId}.json`);fs.writeFileSync(inputSource,'{}');fs.linkSync(inputSource,input);const submitted=command(['submit',run.runId,input]);expect(submitted.status).not.toBe(0);expect(submitted.stderr).toContain('one bounded regular file');const readable=path.join(dir,'readable','src','users.ts'),outside=path.join(root,`linked-readable-${run.runId}`);fs.linkSync(readable,outside);const read=command(['read',run.runId,'src/users.ts']);expect(read.status).not.toBe(0);expect(read.stderr).toContain('one bounded regular file');});
test.skipIf(process.platform==='win32')('rejects a SARIF FIFO without blocking on open',()=>{const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),fifo=path.join(root,`sarif-${run.runId}.pipe`);expect(spawnSync('/usr/bin/mkfifo',[fifo],{encoding:'utf8',timeout:5_000}).status).toBe(0);const started=Date.now(),imported=command(['import-sarif',run.runId,fifo]);expect(Date.now()-started).toBeLessThan(2_000);expect(imported.status).not.toBe(0);expect(imported.stderr).toContain('bounded regular file');});
test('model submissions cannot overwrite helper-owned readiness or scanner coverage',()=>{const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),file=path.join(root,'helper-coverage.json');fs.writeFileSync(file,JSON.stringify({coverage:[{domain:'runtime-readiness',scope:'node',status:'assessed',method:'claimed by model',gaps:[],exclusions:[],evidence:['untrusted claim']}]}));const result=command(['submit',run.runId,file]);expect(result.status).not.toBe(0);expect(result.stderr).toContain('helper-owned');const report=JSON.parse(fs.readFileSync(path.join(state,'security','cso',run.repoId,run.runId,'report.json'),'utf8'));expect(report.coverage.some((x:any)=>x.domain==='runtime-readiness')).toBe(false);});
test('submission typos are rejected instead of silently weakening evidence',()=>{const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),file=path.join(root,'typo-submission.json');for(const value of [{finding:[]},{modelUsage:{source:'host',tokens:1,tokenz:1}},{application:{actors:['a'],assets:['b'],entrypoints:['c'],tenantBoundaries:['d'],sensitiveOperations:['e'],invariants:['f'],unknown:['g']}}]){fs.writeFileSync(file,JSON.stringify(value));const result=command(['submit',run.runId,file]);expect(result.status).not.toBe(0);expect(result.stderr).toContain('Unexpected');}});
test('submission collections and numeric usage fail with precise schema errors',()=>{const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),file=path.join(root,'invalid-submission-shape.json');for(const [body,message] of [['{"findings":{}}','submission.findings must be an array'],['{"coverage":{}}','submission.coverage must be an array'],['{"modelUsage":{"source":"host","tokens":0,"cost":1e400}}','finite nonnegative']]){fs.writeFileSync(file,body);const result=command(['submit',run.runId,file]);expect(result.status).not.toBe(0);expect(result.stderr).toContain(message);}});
test('the reserved reporting minute accepts evidence until the actual deadline',()=>{const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),reportPath=path.join(state,'security','cso',run.repoId,run.runId,'report.json'),report=JSON.parse(fs.readFileSync(reportPath,'utf8')),file=path.join(root,`reporting-reserve-${run.runId}.json`);fs.writeFileSync(file,'{}');report.deadline=new Date(Date.now()+30_000).toISOString();fs.writeFileSync(reportPath,JSON.stringify(report,null,2)+'\n');expect(command(['submit',run.runId,file]).status).toBe(0);report.deadline=new Date(Date.now()-1_000).toISOString();fs.writeFileSync(reportPath,JSON.stringify(report,null,2)+'\n');const expired=command(['submit',run.runId,file]);expect(expired.status).not.toBe(0);expect(expired.stderr).toContain('Audit deadline reached');});
test('explicit base pins history even when finding scope is not diff-only',()=>{const head=git('rev-parse','HEAD').trim(),started=command(['start','--repo',repo,'--scope','auth','--offline','--base','HEAD']);expect(started.status).toBe(0);const run=JSON.parse(started.stdout),dir=path.join(state,'security','cso',run.repoId,run.runId),report=JSON.parse(fs.readFileSync(path.join(dir,'report.json'),'utf8')),history=JSON.parse(fs.readFileSync(path.join(dir,'history-status.json'),'utf8'));expect(report.policy.diff).toBe(false);expect(report.source.baseCommit).toBe(head);expect(history.range).toBe(`${head}..${head}`);const invalid=command(['start','--repo',repo,'--scope','auth','--offline','--base','missing-ref']);expect(invalid.status).not.toBe(0);expect(invalid.stderr).toContain('MISSING_INPUT');});
test('an optional scanner prerequisite does not turn completed manual coverage partial',()=>{
const run=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout);
const scanned=command(['scan',run.runId,'gitleaks']);expect(scanned.status).toBe(0);expect(JSON.parse(scanned.stdout).status).toBe('not_assessed');
const reportPath=path.join(state,'security','cso',run.repoId,run.runId,'report.json'),report=JSON.parse(fs.readFileSync(reportPath,'utf8'));
const input=path.join(root,`manual-${run.runId}.json`);fs.writeFileSync(input,JSON.stringify({application:{actors:['tenant user'],assets:['tenant records'],entrypoints:['GET /users/:id'],tenantBoundaries:['tenant id'],sensitiveOperations:['record read'],invariants:['tenant isolation']},coverage:report.coverage.filter((c:any)=>!c.domain.startsWith('scanner:')&&!['snapshot-inputs','history-inputs'].includes(c.domain)).map((c:any)=>({...c,status:'assessed',method:'manual source and caller review',gaps:[],evidence:['fresh captured-source trace']})),gaps:[]}));
expect(command(['submit',run.runId,input]).status).toBe(0);expect(command(['finish',run.runId]).status).toBe(0);const final=JSON.parse(fs.readFileSync(reportPath,'utf8'));expect(final.completeness).toBe('complete');expect(final.coverage.find((c:any)=>c.domain==='scanner:gitleaks').status).toBe('not_assessed');
});
test('unread tracked source is a disclosed helper-owned material gap',()=>{
const binaryRepo=path.join(root,'binary-repo'),runGit=(...args:string[])=>{const result=spawnSync('/usr/bin/git',['-C',binaryRepo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);};
fs.mkdirSync(binaryRepo);runGit('init','-q');runGit('config','user.email','fixture@example.test');runGit('config','user.name','Fixture');
fs.writeFileSync(path.join(binaryRepo,'app.js'),'export const ready = true\n');fs.writeFileSync(path.join(binaryRepo,'auth.wasm'),Buffer.from([0,97,115,109,1,0,0,0]));
runGit('add','app.js','auth.wasm');runGit('commit','-qm','binary security boundary');
const started=command(['start','--repo',binaryRepo,'--scope','auth','--offline']);expect(started.status).toBe(0);
const run=JSON.parse(started.stdout),reportPath=path.join(state,'security','cso',run.repoId,run.runId,'report.json'),initial=JSON.parse(fs.readFileSync(reportPath,'utf8'));
expect(initial.coverage.find((item:any)=>item.domain==='snapshot-inputs')).toMatchObject({status:'partial',gaps:['auth.wasm: in-scope source payload was unread and withheld from static and runtime assessment']});
expect(initial.source.transformations).toContainEqual({path:'auth.wasm',handling:'withheld: binary or redaction failed'});
const override=path.join(root,`snapshot-override-${run.runId}.json`);fs.writeFileSync(override,JSON.stringify({coverage:[{domain:'snapshot-inputs',scope:'domain:auth',status:'assessed',method:'model claim',gaps:[],exclusions:[],evidence:['claimed complete']}]}));
const rejected=command(['submit',run.runId,override]);expect(rejected.status).not.toBe(0);expect(rejected.stderr).toContain('helper-owned');
const evidence=path.join(root,`binary-evidence-${run.runId}.json`);fs.writeFileSync(evidence,JSON.stringify({application:{actors:['authenticated user'],assets:['authorization policy'],entrypoints:['application request'],tenantBoundaries:['authorization boundary'],sensitiveOperations:['protected operation'],invariants:['authorization is enforced']},coverage:initial.coverage.filter((item:any)=>!['snapshot-inputs','history-inputs'].includes(item.domain)).map((item:any)=>({...item,status:'assessed',method:'captured source review',gaps:[],evidence:['reviewed readable captured source']})),gaps:[]}));
expect(command(['submit',run.runId,evidence]).status).toBe(0);expect(command(['finish',run.runId]).status).toBe(0);
const final=JSON.parse(fs.readFileSync(reportPath,'utf8')),markdown=fs.readFileSync(path.join(path.dirname(reportPath),'report.md'),'utf8');
expect(final.completeness).toBe('partial');expect(final.coverage.find((item:any)=>item.domain==='snapshot-inputs').status).toBe('partial');
expect(markdown.startsWith('partial — domain:auth')).toBe(true);expect(markdown).toContain('auth.wasm: in-scope source payload was unread');expect(markdown).toContain('auth.wasm: withheld: binary or redaction failed');
});
test('a surviving root-cause variant leaves the fresh recheck open for correction',()=>{const original=JSON.parse(command(['start','--repo',repo,'--scope','auth','--offline']).stdout),rootCause='Tenant query omits caller tenant predicate',finding={title:'Cross-tenant user read',rootCause,location:{path:'src/users.ts',line:1,symbol:'tenantQuery'},advisoryIds:[],severity:'high',confidence:'high',confidenceRationale:'The captured caller-to-sink trace and control review directly support the finding',evidence:'supported',attackerControl:'Authenticated caller chooses the record ID',impact:'Another tenant record is returned',scenario:'A tenant supplies a known record ID owned by another tenant and receives that record',trace:['GET /users/:id','tenantQuery','findUnique by id'],references:['src/users.ts:1','OWASP API1:2023'],recommendation:'Bind the lookup predicate to the authenticated tenant identifier before returning the record',challenge:{reviewer:'independent-2',independent:true,mode:'independent_agent',callers:'Authenticated route forwards path ID',controls:'Authentication exists; authorization predicate does not',counterevidence:'Opaque IDs reduce guessing but do not authorize',conclusion:'The caller can provide a known cross-tenant ID'}},application={actors:['tenant user'],assets:['records'],entrypoints:['GET /users/:id'],tenantBoundaries:['tenant id'],sensitiveOperations:['record read'],invariants:['tenant isolation']},reportPath=path.join(state,'security','cso',original.repoId,original.runId,'report.json'),report=JSON.parse(fs.readFileSync(reportPath,'utf8')),submission=path.join(root,'recheck-original.json');fs.writeFileSync(submission,JSON.stringify({application,findings:[finding],coverage:report.coverage.filter((c:any)=>!['snapshot-inputs','history-inputs'].includes(c.domain)).map((c:any)=>({...c,status:'assessed',gaps:[],method:'fresh trace',evidence:['captured source']})),gaps:[]}));expect(command(['submit',original.runId,submission]).status).toBe(0);expect(command(['finish',original.runId]).status).toBe(0);const findingId=JSON.parse(fs.readFileSync(reportPath,'utf8')).findings[0].id,rechecked=command(['recheck',findingId,'--run',original.runId,'--repo',repo]);expect(rechecked.status).toBe(0);const child=JSON.parse(rechecked.stdout),childPath=path.join(state,'security','cso',original.repoId,child.runId,'report.json'),childReport=JSON.parse(fs.readFileSync(childPath,'utf8')),claim=path.join(root,'recheck-claim.json'),variant={...finding,title:'Moved cross-tenant user read',location:{...finding.location,symbol:'movedTenantQuery'}};fs.writeFileSync(claim,JSON.stringify({application,findings:[variant],coverage:childReport.coverage.filter((c:any)=>!['snapshot-inputs','history-inputs'].includes(c.domain)).map((c:any)=>({...c,status:'assessed',gaps:[],method:'fresh current-source trace',evidence:['new snapshot']})),gaps:[],recheck:{findingId,outcome:'resolved',evidence:[{kind:'caller',path:'src/users.ts',line:1,observation:'Fresh caller trace reaches the tenant predicate'},{kind:'security_boundary',path:'src/users.ts',line:1,observation:'Fresh boundary trace checks cross-tenant denial'}],rootCause}}));expect(command(['submit',child.runId,claim]).status).toBe(0);const finished=command(['finish',child.runId]);expect(finished.status).not.toBe(0);expect(finished.stderr).toContain('surviving root-cause');expect(JSON.parse(fs.readFileSync(childPath,'utf8')).status).toBe('running');});
test('doctor performs no downloads and rejects a remote Docker endpoint',()=>{const r=command(['doctor','--repo',repo],{DOCKER_HOST:'tcp://127.0.0.1:2375'});expect(r.status).toBe(0);const value=JSON.parse(r.stdout);expect(value.downloads).toBe(false);expect(value.checks.find((x:any)=>x.capability==='local-docker-isolation')).toMatchObject({status:'missing'});expect(JSON.stringify(value)).toContain('Remote TCP');});
test('doctor returns exact missing capabilities for an unavailable repository',()=>{const r=command(['doctor','--repo',path.join(root,'missing-repository')],{DOCKER_HOST:'tcp://127.0.0.1:2375'});expect(r.status).toBe(0);const value=JSON.parse(r.stdout);expect(value.elapsedMs).toBeLessThan(30_000);expect(value.checks.find((x:any)=>x.capability==='static-snapshot')).toMatchObject({status:'missing'});expect(value.checks.find((x:any)=>x.capability==='application-preparation')).toMatchObject({status:'missing'});expect(value.checks.find((x:any)=>x.capability==='qualified-runtimes')).toMatchObject({status:'missing'});});
test('audits nonignored source in a newly initialized repository before its first commit',()=>{
const unborn=path.join(root,'unborn-repo');fs.mkdirSync(unborn);const runGit=(...args:string[])=>{const result=spawnSync('/usr/bin/git',['-C',unborn,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);};runGit('init','-q');fs.writeFileSync(path.join(unborn,'app.js'),'export const newProject = true\n');
const diagnosed=command(['doctor','--repo',unborn],{DOCKER_HOST:'tcp://127.0.0.1:2375'});expect(diagnosed.status).toBe(0);expect(JSON.parse(diagnosed.stdout).checks.find((item:any)=>item.capability==='static-snapshot')).toMatchObject({status:'ready'});
const started=command(['start','--repo',unborn,'--scope','auth','--offline']);expect(started.status).toBe(0);const run=JSON.parse(started.stdout),dir=path.join(state,'security','cso',run.repoId,run.runId),manifest=JSON.parse(fs.readFileSync(path.join(dir,'snapshot.json'),'utf8')),history=JSON.parse(fs.readFileSync(path.join(dir,'history-status.json'),'utf8'));
expect(manifest).not.toHaveProperty('headCommit');expect(manifest.entries.find((item:any)=>item.path==='app.js')?.executionHash).toMatch(/^[a-f0-9]{64}$/);expect(history).toEqual({status:'captured',range:'unborn HEAD',commits:0,bytes:0});
});
test('state configured inside the audited repository is rejected before changing it',()=>{const before=git('status','--porcelain=v1','-z'),r=command(['start','--repo',repo],{GSTACK_HOME:path.join(repo,'.private-state')});expect(r.status).not.toBe(0);expect(r.stderr).toContain('UNSAFE_PATH');expect(fs.existsSync(path.join(repo,'.private-state'))).toBe(false);expect(git('status','--porcelain=v1','-z')).toBe(before);});
});
File diff suppressed because one or more lines are too long
+471
View File
@@ -0,0 +1,471 @@
import { afterEach, describe, expect, test } from 'bun:test';
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
import { imageBuildMatrix } from '../scripts/cso-image-matrix';
import { runBashScript } from './helpers/bash-script';
const ROOT = resolve(import.meta.dir, '..');
const temps: string[] = [];
afterEach(() => { for (const dir of temps.splice(0)) rmSync(dir, { recursive: true, force: true }); });
const quote = (s: string) => "'" + s.replaceAll("'", "'\\''") + "'";
const CREDENTIAL_IMAGE = ['https://user:', 'pass@example.test/node'].join('');
function buildFixture() {
const dir = mkdtempSync(join(tmpdir(), 'gstack-cso-build-')); temps.push(dir);
for (const sub of ['scripts', 'bin', 'lib/cso']) mkdirSync(join(dir, sub), { recursive: true });
copyFileSync(join(ROOT, 'scripts/build-cso.sh'), join(dir, 'scripts/build-cso.sh'));
const publisher = [
'#!/bin/sh',
'set -eu',
'directory=$1',
'shift',
'if [ "$(uname -s)" = Linux ]; then',
" exec /usr/bin/flock -n -E 73 \"$directory\" /bin/sh -c '",
' if [ -n "${CSO_PUBLISH_BARRIER:-}" ]; then',
' : > "${CSO_PUBLISH_BARRIER}.ready"',
' while [ ! -f "${CSO_PUBLISH_BARRIER}.go" ]; do sleep .01; done',
' fi',
' export GSTACK_CSO_PUBLISH_LOCKED=1',
' exec "$@"',
" ' cso-publish \"$@\"",
'fi',
'export GSTACK_CSO_PUBLISH_LOCKED=1',
'exec "$@"',
'',
].join('\n');
writeFileSync(join(dir, 'publish lock wrapper'), publisher); chmodSync(join(dir, 'publish lock wrapper'), 0o755);
// The recorder substitutes compilation only. The build command itself is the
// real script, so quoting, flag propagation and failed-compiler behavior run.
const recorder = '#!/bin/sh\nset -eu\nprintf "%s\\n" "$@" >> "$CSO_BUILD_LOG"\ncount_file="$CSO_BUILD_LOG.count"\ncount=0\n[ ! -f "$count_file" ] || count=$(cat "$count_file")\ncount=$((count+1))\nprintf "%s\\n" "$count" > "$count_file"\nif [ -n "${CSO_BUILD_BARRIER:-}" ] && [ "$count" = 1 ]; then touch "$CSO_BUILD_BARRIER.ready"; while [ ! -f "$CSO_BUILD_BARRIER.go" ]; do sleep .01; done; fi\nif [ "${CSO_FAIL_COMPILER:-0}" = 1 ] || [ "${CSO_FAIL_COMPILER_N:-0}" = "$count" ]; then exit 42; fi\noutput=""\nwhile [ "$#" -gt 0 ]; do\n case "$1" in --outfile|-o) shift; output="$1" ;; esac\n shift\ndone\nif [ -n "$output" ]; then\n case "$output" in\n *gstack-cso-publish-lock*) cp "$CSO_FAKE_LOCKER_TEMPLATE" "$output" ;;\n *) generation="${CSO_BUILD_GENERATION:-NEW}"; cat > "$output" <<EOF\n#!/bin/sh\nprintf "%s\\n" "$generation"\nEOF\n ;;\n esac\n chmod +x "$output"\nfi\n';
writeFileSync(join(dir, 'compiler recorder'), recorder); chmodSync(join(dir, 'compiler recorder'), 0o755);
return dir;
}
function seedBundle(dir:string,generation='OLD'){
for(const name of ['gstack-cso-launcher','gstack-cso-core','gstack-cso-watchdog']){const file=join(dir,'bin',name);writeFileSync(file,`#!/bin/sh\nprintf '%s\\n' '${generation}'\n`);chmodSync(file,0o755);}
writeFileSync(join(dir,'bin','.gstack-cso-generation'),'0'.repeat(64)+'\n');
}
function bundleContents(dir:string){return ['gstack-cso-launcher','gstack-cso-core','gstack-cso-watchdog','.gstack-cso-generation'].map(name=>readFileSync(join(dir,'bin',name),'utf8'));}
function expectPublishedBundle(dir:string,generation='NEW'){
const contents=bundleContents(dir);
expect(contents.slice(0,3).every(content=>content.includes(generation))).toBe(true);
expect(contents[3]).toMatch(/^[a-f0-9]{64}\n$/);
}
function fakeBuildCommand(dir:string,extra=''){const log=join(dir,'compiler.log');return `${extra} BUN_CMD=${quote(join(dir,'compiler recorder'))} CSO_CC=${quote(join(dir,'compiler recorder'))} CSO_BUILD_LOG=${quote(log)} CSO_FAKE_LOCKER_TEMPLATE=${quote(join(dir,'publish lock wrapper'))} bash ${quote(join(dir,'scripts/build-cso.sh'))}`;}
function fakeBuild(dir:string,extra=''){return runBashScript(fakeBuildCommand(dir,extra),{timeout:10_000});}
function reviewedInputs(): any {
const pinned = (repository: string, fill: string) => `${repository}@sha256:${fill.repeat(64)}`;
const family = (repository: string, tag: string, index: string, amd64: string, arm64: string) => ({
source: `${repository}:${tag}`,
indexImage: pinned(repository, index),
images: { 'linux/amd64': pinned(repository, amd64), 'linux/arm64': pinned(repository, arm64) },
});
const sbom = family('docker.io/tool/sbom', '1.0.0', '1', '2', '3');
const base = (repository: string, tag: string, index: string, amd64: string, arm64: string) => {
const value = family(repository, tag, index, amd64, arm64);
return { source: value.source, indexImage: value.indexImage, baseImages: value.images };
};
return {
schemaVersion: 1, helperAbi: 3, state: 'reviewed', revision: 'fixture-runtime-inputs',
reviewedAt: '2026-09-10T00:00:00.000Z',
reviewMethod: 'Fixture review metadata long enough to exercise the strict source-controlled build input contract.',
sbomGenerator: sbom,
profiles: [
{ id: 'node-24.4.0', stack: 'node', ...base('docker.io/library/node', '24.4.0', '4', '5', '6'), versions: { node: '24.4.0', npm: '11.4.2', 'cso-preparation': '1.0.0' } },
{ id: 'bun-1.3.10', stack: 'bun', ...base('docker.io/oven/bun', '1.3.10', '7', '8', '9'), versions: { bun: '1.3.10', 'cso-preparation': '1.0.0' } },
{ id: 'python-3.13.4-uv-0.8.0', stack: 'python', ...base('docker.io/library/python', '3.13.4', 'a', 'b', 'c'),
uvSource: 'ghcr.io/astral-sh/uv:0.8.0', uvIndexImage: pinned('ghcr.io/astral-sh/uv', 'd'),
uvImages: { 'linux/amd64': pinned('ghcr.io/astral-sh/uv', 'e'), 'linux/arm64': pinned('ghcr.io/astral-sh/uv', 'f') },
versions: { python: '3.13.4', uv: '0.8.0', 'cso-preparation': '1.0.0' } },
{ id: 'rails-ruby-3.4.4', stack: 'rails', ...base('docker.io/library/ruby', '3.4.4', '0', 'a', 'b'), versions: { ruby: '3.4.4', bundler: '2.6.7', 'cso-preparation': '1.0.0' } },
{ id: 'postgresql-17.2', stack: 'postgresql', ...base('docker.io/library/postgres', '17.2', 'c', 'd', 'e'), versions: { postgresql: '17.2' } },
],
};
}
describe('CSO build and distribution wiring', () => {
test('POSIX helpers expose Darwin no-follow flags before system headers', () => {
for (const relative of ['lib/cso/launcher.c', 'lib/cso/watchdog.c', 'lib/cso/publish-lock.c']) {
const source = readFileSync(join(ROOT, relative), 'utf8');
const darwinFeature = source.indexOf('#define _DARWIN_C_SOURCE 1');
const fileFlags = source.indexOf('#include <fcntl.h>');
expect(darwinFeature, relative).toBeGreaterThanOrEqual(0);
expect(fileFlags, relative).toBeGreaterThan(darwinFeature);
expect(source, relative).toContain('O_NOFOLLOW');
}
});
test('the cached eval image proves the static C toolchain required by direct builds', () => {
const dockerfile = readFileSync(join(ROOT, '.github/docker/Dockerfile.ci'), 'utf8');
expect(dockerfile).toMatch(/\bgcc libc6-dev\b/);
expect(dockerfile).toContain('cc -std=c11 -static /tmp/gstack-cso-cc-probe.c');
expect(dockerfile).toContain('/tmp/gstack-cso-cc-probe');
});
test.skipIf(process.platform === 'win32')('real build script supplies all startup-hardening flags and compiles its watchdog', () => {
const dir = buildFixture(), log = join(dir, 'compiler.log');
const r = fakeBuild(dir);
expect(r.status).toBe(0);
const args = readFileSync(log, 'utf8').split('\n');
for (const flag of ['--no-compile-autoload-dotenv', '--no-compile-autoload-bunfig', '--no-compile-autoload-tsconfig', '--no-compile-autoload-package-json']) expect(args.filter(a => a === flag)).toHaveLength(1);
expect(args).toContain('lib/cso/cli.ts');
expect(args.some(arg=>/bin\/\.gstack-cso-stage\.[^/]+\/gstack-cso-core$/.test(arg))).toBe(true);
expect(args).toContain('lib/cso/launcher.c');
expect(args.some(arg=>/bin\/\.gstack-cso-stage\.[^/]+\/gstack-cso-launcher$/.test(arg))).toBe(true);
expect(args).toContain('lib/cso/watchdog.c');
expect(args.some(arg=>/bin\/\.gstack-cso-stage\.[^/]+\/gstack-cso-watchdog$/.test(arg))).toBe(true);
expect(args).toContain('lib/cso/publish-lock.c');
expect(args.some(arg=>/bin\/\.gstack-cso-stage\.[^/]+\/gstack-cso-publish-lock$/.test(arg))).toBe(true);
expect(spawnSync(join(dir,'bin/gstack-cso-launcher'),['--version'],{encoding:'utf8',timeout:5000}).stdout.trim()).toBe('NEW');
expectPublishedBundle(dir);
});
test.skipIf(process.platform === 'win32')('generation validation accepts BSD wc padding', () => {
const dir = buildFixture(), tools = join(dir, 'bsd-tools'), wc = join(tools, 'wc');
mkdirSync(tools);
writeFileSync(wc, '#!/bin/sh\nset -eu\ncount=$(/usr/bin/wc -c "$@")\nprintf " %s\\n" "$count"\n');
chmodSync(wc, 0o755);
const result = fakeBuild(dir, `PATH=${quote(`${tools}:/usr/bin:/bin`)}`);
expect(result.status).toBe(0);
expectPublishedBundle(dir);
});
test.skipIf(process.platform === 'win32').each([1,2,3,4])('compiler failure at stage %i preserves the exact runnable old bundle', failure => {
const dir=buildFixture();seedBundle(dir);const before=bundleContents(dir);
const r=fakeBuild(dir,`CSO_FAIL_COMPILER_N=${failure}`);
expect(r.status).toBe(42);
expect(bundleContents(dir)).toEqual(before);
expect(spawnSync(join(dir,'bin/gstack-cso-launcher'),{encoding:'utf8',timeout:5000}).stdout.trim()).toBe('OLD');
});
test.skipIf(process.platform === 'win32').each(['withdraw-launcher','publish-core','publish-watchdog','before-publish-launcher','publish-launcher'])('publication failure after %s rolls back to the exact old bundle', checkpoint=>{
const dir=buildFixture();seedBundle(dir);const before=bundleContents(dir);
const r=fakeBuild(dir,`GSTACK_CSO_BUILD_TESTING=1 GSTACK_CSO_BUILD_TEST_FAIL_AFTER=${checkpoint}`);
expect(r.status).toBe(86);expect(bundleContents(dir)).toEqual(before);
expect(spawnSync(join(dir,'bin/gstack-cso-launcher'),{encoding:'utf8',timeout:5000}).stdout.trim()).toBe('OLD');
});
test.skipIf(process.platform === 'win32')('SIGKILL before launcher publication leaves no runnable mixed bundle',()=>{
const dir=buildFixture();seedBundle(dir);const r=fakeBuild(dir,'GSTACK_CSO_BUILD_TESTING=1 GSTACK_CSO_BUILD_TEST_KILL_AFTER=publish-core');
expect(r.status).not.toBe(0);expect(() => readFileSync(join(dir,'bin/gstack-cso-launcher'))).toThrow();
expect(fakeBuild(dir).status).toBe(0);
expectPublishedBundle(dir);
});
test.skipIf(process.platform === 'win32')('SIGKILL after launcher-last publication leaves a complete new bundle',()=>{
const dir=buildFixture();seedBundle(dir);const r=fakeBuild(dir,'GSTACK_CSO_BUILD_TESTING=1 GSTACK_CSO_BUILD_TEST_KILL_AFTER=publish-launcher');
expect(r.status).not.toBe(0);expectPublishedBundle(dir);
expect(spawnSync(join(dir,'bin/gstack-cso-launcher'),{encoding:'utf8',timeout:5000}).stdout.trim()).toBe('NEW');
});
test.skipIf(process.platform === 'win32')('a failed rollback withholds the launcher and retains recovery material',()=>{
const dir=buildFixture();seedBundle(dir);
const r=fakeBuild(dir,'GSTACK_CSO_BUILD_TESTING=1 GSTACK_CSO_BUILD_TEST_FAIL_AFTER=publish-core GSTACK_CSO_BUILD_TEST_FAIL_RESTORE=core');
expect(r.status).not.toBe(0);expect(() => readFileSync(join(dir,'bin/gstack-cso-launcher'))).toThrow();
expect(r.stderr).toContain('recovery files remain');
expect(readFileSync(join(dir,'bin/gstack-cso-watchdog'),'utf8')).toContain('OLD');
});
test.skipIf(process.platform !== 'linux')('publisher lock rejects a competing OS lock without mutating the installed bundle',async()=>{
const dir=buildFixture(),ready=join(dir,'lock.ready'),release=join(dir,'lock.release');seedBundle(dir);const before=bundleContents(dir);
const holder=Bun.spawn(['/usr/bin/flock','-n',join(dir,'bin'),'/bin/sh','-c',`: > ${quote(ready)}; while [ ! -f ${quote(release)} ]; do sleep .01; done`],{stdout:'pipe',stderr:'pipe'});
const deadline=Date.now()+3000;while(!existsSync(ready)&&Date.now()<deadline)await Bun.sleep(10);
expect(existsSync(ready)).toBe(true);
expect(fakeBuild(dir).status).toBe(73);
expect(bundleContents(dir)).toEqual(before);
writeFileSync(release,'go');expect(await holder.exited).toBe(0);
});
test.skipIf(process.platform === 'win32')('an ownerless legacy mkdir lock cannot block publication',()=>{
const dir=buildFixture();seedBundle(dir);mkdirSync(join(dir,'bin/.gstack-cso-build.lock'));
expect(fakeBuild(dir).status).toBe(0);
expectPublishedBundle(dir);
});
test.skipIf(process.platform !== 'linux')('concurrent publishers cannot interleave generations under the exclusive publication lock',async()=>{
const dir=buildFixture(),barrier=join(dir,'barrier');seedBundle(dir);
const first=Bun.spawn(['/bin/bash','-c',fakeBuildCommand(dir,`CSO_PUBLISH_BARRIER=${quote(barrier)} CSO_BUILD_GENERATION=A`)],{stdout:'pipe',stderr:'pipe'});
const deadline=Date.now()+3000;while(!existsSync(`${barrier}.ready`)&&Date.now()<deadline)await Bun.sleep(10);
expect(existsSync(`${barrier}.ready`)).toBe(true);
const second=fakeBuild(dir,'CSO_BUILD_GENERATION=B');expect(second.status).toBe(73);
writeFileSync(`${barrier}.go`,'go');expect(await first.exited).toBe(0);
expectPublishedBundle(dir,'A');
});
test.skipIf(process.platform === 'win32')('a signal delivered after mv but before it returns cannot expose a mixed bundle',()=>{
const dir=buildFixture(),tools=join(dir,'tools'),marker=join(dir,'signal-after-mv');seedBundle(dir);mkdirSync(tools);
const realMv=Bun.which('mv');expect(realMv).toBeTruthy();
writeFileSync(join(tools,'mv'),`#!/bin/sh\n${quote(realMv!)} "$@"\nstatus=$?\nif [ "$status" -eq 0 ] && [ ! -f "$CSO_SIGNAL_AFTER_MV_MARKER" ]; then\n : > "$CSO_SIGNAL_AFTER_MV_MARKER"\n kill -TERM "$PPID"\n sleep .05\nfi\nexit "$status"\n`);
chmodSync(join(tools,'mv'),0o755);
const r=fakeBuild(dir,`PATH=${quote(`${tools}:/usr/bin:/bin`)} CSO_SIGNAL_AFTER_MV_MARKER=${quote(marker)}`);
expect(r.status).toBe(0);expect(existsSync(marker)).toBe(true);expectPublishedBundle(dir);
});
test.skipIf(process.platform !== 'linux')('a macOS build fails closed when hardened-runtime signing is unavailable', () => {
const dir = buildFixture(), log = join(dir, 'compiler.log'), tools = join(dir, 'tools');
seedBundle(dir);const before=bundleContents(dir);
mkdirSync(tools);
writeFileSync(join(tools, 'uname'), '#!/bin/sh\nprintf "Darwin\\n"\n');
chmodSync(join(tools, 'uname'), 0o755);
const r = runBashScript(`PATH=${quote(`${tools}:/usr/bin:/bin`)} BUN_CMD=${quote(join(dir, 'compiler recorder'))} CSO_CC=${quote(join(dir, 'compiler recorder'))} CSO_BUILD_LOG=${quote(log)} bash ${quote(join(dir, 'scripts/build-cso.sh'))}`, { timeout: 10_000 });
expect(r.status).toBe(1);
expect(r.stderr).toContain('requires macOS codesign');
expect(bundleContents(dir)).toEqual(before);
});
test.skipIf(process.platform !== 'linux')('a macOS signing failure cannot mutate the installed generation',()=>{
const dir=buildFixture(),log=join(dir,'compiler.log'),tools=join(dir,'tools');seedBundle(dir);const before=bundleContents(dir);mkdirSync(tools);
writeFileSync(join(tools,'uname'),'#!/bin/sh\nprintf "Darwin\\n"\n');writeFileSync(join(tools,'codesign'),'#!/bin/sh\nexit 1\n');
chmodSync(join(tools,'uname'),0o755);chmodSync(join(tools,'codesign'),0o755);
const r=runBashScript(`PATH=${quote(`${tools}:/usr/bin:/bin`)} BUN_CMD=${quote(join(dir,'compiler recorder'))} CSO_CC=${quote(join(dir,'compiler recorder'))} CSO_BUILD_LOG=${quote(log)} bash ${quote(join(dir,'scripts/build-cso.sh'))}`,{timeout:10_000});
expect(r.status).not.toBe(0);expect(bundleContents(dir)).toEqual(before);
});
test('setup distributes the helper pair without promising an incomplete npm bin', () => {
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
const build = readFileSync(join(ROOT, 'scripts/build.sh'), 'utf8');
const setup = readFileSync(join(ROOT, 'setup'), 'utf8');
const ignore = readFileSync(join(ROOT, '.gitignore'), 'utf8').split('\n');
expect(pkg.bin).not.toHaveProperty('gstack-cso');
expect(pkg.scripts['build:cso']).toBe('bash scripts/build-cso.sh');
expect(pkg.scripts['test:cso:macos']).toBe('bun test test/cso-macos-launcher.test.ts test/cso-registry-socket.test.ts');
expect(pkg.scripts['test:cso:docker']).toContain('test/cso-node-lifecycle-integration.test.ts');
expect(pkg.scripts['test:cso:docker']).toContain('test/cso-stack-cold-integration.test.ts');
expect(pkg.scripts['test:cso:docker']).toContain('--max-concurrency 1');
expect(build).toContain('bash scripts/build-cso.sh');
const signed = setup.match(/for _bin in ([^;]+);/)![1].split(/\s+/);
for (const name of ['bin/gstack-cso-launcher','bin/gstack-cso-core', 'bin/gstack-cso-watchdog']) {
expect(signed).not.toContain(name); expect(ignore).toContain(name); expect(ignore).toContain(`${name}.exe`);
}
expect(readFileSync(join(ROOT,'scripts/build-cso.sh'),'utf8')).toContain('cso_sign_macos_artifact "$CSO_STAGE_LAUNCHER" 1');
for (const name of ['lib/cso/images/gstack-cso-verifier', 'lib/cso/images/gstack-cso-preparation']) expect(ignore).toContain(name);
expect(setup).toContain('gstack-cso-launcher$_EXE" provision-images --setup-summary');
expect(setup).toContain('anonymous exact-digest pulls');
expect(setup).toContain('static audits remain available');
// Host skill installers already distribute bin and lib together.
expect(/for asset in bin lib browse review qa/.test(setup)).toBe(true);
});
});
describe('CSO runtime staging gates', () => {
test('runtime workflow shell blocks parse after GitHub expressions are substituted', () => {
for (const relative of [
'.github/workflows/cso-runtime-images.yml',
'.github/workflows/cso-runtime-qualification.yml',
'.github/workflows/cso-runtime-promote.yml',
]) {
const raw = readFileSync(join(ROOT, relative), 'utf8');
const workflow = Bun.YAML.parse(raw) as any;
expect(raw).not.toMatch(/\s\+\s+--(?:no-|name|arg|evidence|output|security)/);
for (const job of Object.values(workflow.jobs) as any[]) for (const step of job.steps ?? []) {
if (typeof step.run !== 'string') continue;
const script = step.run.replace(/\$\{\{[\s\S]*?\}\}/g, 'GH_EXPR');
const parsed = spawnSync('/bin/bash', ['-n'], { input: script, encoding: 'utf8', timeout: 30_000 });
expect(parsed.status, `${relative}: ${step.name ?? step.run}\n${parsed.stderr}`).toBe(0);
}
}
});
test('reviewed inputs produce every application and database stack on each native Linux architecture', () => {
const matrix = imageBuildMatrix(reviewedInputs());
expect(matrix.include).toHaveLength(10);
for (const stack of ['node', 'bun', 'python', 'rails', 'postgresql']) {
const rows = matrix.include.filter(r => r.stack === stack);
expect(rows.map(r => r.platform)).toEqual(['linux/amd64', 'linux/arm64']);
expect(rows.map(r => r.runner)).toEqual(['ubuntu-24.04', 'ubuntu-24.04-arm']);
}
});
test('unreviewed or incomplete inputs cannot silently publish a partial matrix', () => {
const inputs = reviewedInputs(); inputs.state = 'pending';
expect(() => imageBuildMatrix(inputs)).toThrow('MISSING_REVIEWED_BUILD_INPUTS');
inputs.state = 'reviewed'; inputs.profiles.pop();
expect(() => imageBuildMatrix(inputs)).toThrow('INCOMPLETE_STACK_MATRIX');
});
test.each(['node:latest', 'docker.io/library/node:24', CREDENTIAL_IMAGE, 'docker.io/library/node@sha256:bad', 'docker.io/library/node@sha256:' + 'A'.repeat(64), 'docker.io/library/node@sha256:' + 'a'.repeat(64) + '\nBAD=value'])('untrusted base input %s cannot enter the workflow matrix', image => {
const inputs = reviewedInputs(); inputs.profiles[0].baseImages['linux/amd64'] = image;
expect(() => imageBuildMatrix(inputs)).toThrow('UNPINNED_BASE_IMAGE');
});
test('SBOM generator and toolchain inputs must be pinned too', () => {
const inputs = reviewedInputs(); inputs.sbomGenerator.images['linux/amd64'] = 'docker.io/tool/sbom:latest';
expect(() => imageBuildMatrix(inputs)).toThrow('UNPINNED_SBOM_GENERATOR');
const versions = reviewedInputs(); versions.profiles[0].versions.node = '^24';
expect(() => imageBuildMatrix(versions)).toThrow('UNPINNED_TOOL_VERSION');
});
test('duplicate stacks and missing Python uv images fail precisely', () => {
const duplicate = reviewedInputs(); duplicate.profiles[1].stack = 'node';
expect(() => imageBuildMatrix(duplicate)).toThrow('INVALID_STACK');
const python = reviewedInputs(); python.profiles[2].uvImages!['linux/arm64'] = '';
expect(() => imageBuildMatrix(python)).toThrow('UNPINNED_UV_IMAGE');
});
test('dedicated Docker CI is secretless and mandatory when its prerequisites are missing', () => {
const raw = readFileSync(join(ROOT, '.github/workflows/free-tests.yml'), 'utf8');
const workflow = Bun.YAML.parse(raw) as any;
const job = workflow.jobs['cso-docker-integration'];
expect(job).toBeTruthy();
const gate = job.steps.find((step: any) => step.run === 'bun run test:cso:docker');
expect(gate.env.GSTACK_CSO_DOCKER_TESTS).toBe('1');
expect(raw).not.toContain('secrets.');
expect(job.steps.map((s: any) => s.run ?? '').join('\n')).toContain('docker --host unix:///var/run/docker.sock info');
expect(job['continue-on-error']).not.toBe(true);
expect(gate['continue-on-error']).not.toBe(true);
const required = workflow.jobs['free-tests'];
expect(required.if).toBe('always()');
expect(required.needs).toEqual(['free-suite', 'cso-macos-launcher', 'cso-windows-launcher', 'cso-docker-integration']);
expect(required.steps[0].run).toContain('test "$CSO_DOCKER_RESULT" = success');
for (const current of Object.values(workflow.jobs) as any[]) for (const step of current.steps) {
if (step.uses?.startsWith('oven-sh/setup-bun')) expect(step.uses).toBe('oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6');
}
});
test('macOS runs the actual signed launcher gate on a native hosted runner', () => {
const raw = readFileSync(join(ROOT, '.github/workflows/free-tests.yml'), 'utf8');
const workflow = Bun.YAML.parse(raw) as any;
const job = workflow.jobs['cso-macos-launcher'];
expect(job['runs-on']).toBe('macos-latest');
expect(job.steps.some((step: any) => step.run === 'bun run build:cso')).toBe(true);
const gate = job.steps.find((step: any) => step.run === 'bun run test:cso:macos');
expect(gate.env.GSTACK_CSO_MACOS_TESTS).toBe('1');
expect(gate['continue-on-error']).not.toBe(true);
});
test('publication requires manual protected-main review, signed evidence, and native containment checks', () => {
const raw = readFileSync(join(ROOT, '.github/workflows/cso-runtime-images.yml'), 'utf8');
const workflow = Bun.YAML.parse(raw) as any;
expect(Object.keys(workflow.on)).toEqual(['pull_request', 'workflow_dispatch']);
expect(workflow.jobs['validate-native'].if).toContain("github.event_name == 'pull_request'");
expect(workflow.jobs['validate-native'].permissions).toEqual({ contents: 'read' });
expect(workflow.jobs['validate-native'].steps.some((s: any) => s.run?.includes('cso-verify-runtime-base.ts'))).toBe(true);
expect(workflow.jobs['validate-native'].steps.some((s: any) => s.with?.push === false && s.with?.load === true)).toBe(true);
expect(workflow.jobs.stage.if).toContain("github.ref == 'refs/heads/main'");
expect(workflow.jobs.stage.environment).toBe('cso-runtime-release');
const steps = workflow.jobs.stage.steps;
expect(steps.some((s: any) => s.with?.provenance === 'mode=max')).toBe(true);
expect(steps.some((s: any) => s.with?.attests?.includes('generator=${{ matrix.sbomGeneratorImage }}'))).toBe(true);
expect(steps.filter((s: any) => s.uses?.startsWith('actions/attest@'))).toHaveLength(2);
expect(raw).toContain('--source-digest "$GITHUB_SHA"');
expect(raw).toContain('--cert-identity "$signer"');
expect(raw).toContain('--predicate-type https://slsa.dev/provenance/v1');
expect(raw).toContain('--predicate-type https://spdx.dev/Document/v2.3');
expect(raw).toContain('scripts/cso-public-ghcr.ts verify');
expect(raw).toContain('--repository "$GITHUB_REPOSITORY" --output public-image.json');
expect(raw).toContain('sha256sum public-image.json');
expect(raw).toContain('GSTACK_CSO_TEST_IMAGE:');
expect(raw).toContain('GSTACK_CSO_TEST_STACK:');
expect(raw).toContain('bun run test:cso:docker');
expect(workflow.jobs['qualify-native'].needs).toEqual(['reviewed-inputs', 'stage']);
expect(raw).toContain('cso-staged-postgresql-${{ matrix.arch }}');
expect(raw).toContain('GSTACK_CSO_TEST_POSTGRES_IMAGE');
expect(raw).toContain('acquisitionPublicOnlyPassed:true');
expect(raw).toContain('positiveNegativeAssertionsPassed:true');
expect(raw).toContain('heldOutRepairPassed:"pending"');
expect(raw).toContain('railsSqlitePassed:true');
expect(raw).toContain('railsPostgresqlPassed:true');
expect(raw).toContain('nativeExtensionsPassed:true');
expect(raw).toContain('qualified:false');
expect(raw).not.toContain('setup-qemu');
expect(raw).not.toContain('git push');
expect(raw).not.toContain('gh pr create');
expect(raw).not.toContain('contents: write');
const bunDockerfile=readFileSync(join(ROOT,'lib/cso/images/bun.Dockerfile'),'utf8');
const bunPolicy=readFileSync(join(ROOT,'lib/cso/images/bun-no-auto-install.toml'),'utf8');
expect(bunDockerfile).toContain('bun-no-auto-install.toml /opt/cso/no-auto-install.toml');
expect(bunPolicy).toBe('[install]\nauto = "disable"\n');
for (const job of Object.values(workflow.jobs) as any[]) for (const step of job.steps) {
if (step.uses) expect(step.uses).toMatch(/@[a-f0-9]{40}$/);
if (step.uses?.startsWith('oven-sh/setup-bun')) expect(step.uses).toBe('oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6');
}
});
test('catalog promotion authenticates one successful main run and opens an exact attested candidate PR', () => {
const raw = readFileSync(join(ROOT, '.github/workflows/cso-runtime-promote.yml'), 'utf8');
const workflow = Bun.YAML.parse(raw) as any;
const job = workflow.jobs.propose;
expect(Object.keys(workflow.on)).toEqual(['workflow_dispatch']);
expect(job.if).toContain("github.ref == 'refs/heads/main'");
expect(job.environment).toBe('cso-runtime-release');
expect(job.permissions).toMatchObject({ contents: 'write', 'pull-requests': 'write', actions: 'read', packages: 'read', 'id-token': 'write', attestations: 'write' });
expect(raw).toContain('.head_branch == "main"');
expect(raw).toContain('.path == ".github/workflows/cso-runtime-qualification.yml"');
expect(raw).toContain('.event == "repository_dispatch"');
expect(raw).toContain('--name cso-qualified-runtime-statements');
expect(raw).toContain('scripts/cso-runtime-promotion.ts');
expect(raw).toContain('runtime-catalog.candidate.json');
expect(raw).toContain('subject-path: runtime-catalog.candidate.json');
expect(raw).toContain('gh attestation verify runtime-catalog.candidate.json');
expect(raw).toContain('--cert-identity "$signer"');
expect(raw).toContain('--source-digest "$GITHUB_SHA"');
expect(raw).toContain('cso-attestation-evidence.ts digest');
expect(raw).toContain('candidate-attestation-evidence.json');
expect(raw).toContain('scripts/cso-public-ghcr.ts verify');
expect(raw).toContain('--remove-after');
expect(raw).toContain('cso-runtime-promotion.ts validate-transition');
expect(raw).toContain('cmp runtime-catalog.candidate.json committed-runtime-catalog.json');
expect(raw).toContain('git push');
expect(raw).toContain('gh pr create --base main');
expect(raw).toContain('branch="cso-runtime-catalog-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"');
expect(raw).not.toContain('branch="cso-runtime-catalog-$GITHUB_RUN_ID"');
const attested = raw.indexOf('subject-path: runtime-catalog.candidate.json');
const publicPull = raw.indexOf('scripts/cso-public-ghcr.ts verify');
const verified = raw.indexOf('gh attestation verify runtime-catalog.candidate.json');
const committed = raw.indexOf('git add lib/cso/runtime-catalog.json');
expect(publicPull).toBeGreaterThanOrEqual(0);
expect(attested).toBeGreaterThan(publicPull);
expect(verified).toBeGreaterThan(attested);
expect(committed).toBeGreaterThan(verified);
for (const step of job.steps) if (step.uses) expect(step.uses).toMatch(/@[a-f0-9]{40}$/);
});
test('private qualification enters only through an actor-restricted protected environment and re-verifies staged attestations', () => {
const raw = readFileSync(join(ROOT, '.github/workflows/cso-runtime-qualification.yml'), 'utf8');
const workflow = Bun.YAML.parse(raw) as any;
const job = workflow.jobs.qualify;
expect(Object.keys(workflow.on)).toEqual(['repository_dispatch']);
expect(job.environment).toBe('cso-runtime-release');
expect(job.permissions).toEqual({ contents: 'read', packages: 'read', attestations: 'read' });
expect(raw).toContain('test "$GITHUB_ACTOR" = "$CSO_QUALIFICATION_ACTOR"');
expect(raw).toContain('.client_payload.statements | type == "array" and length == 10');
expect(raw).toContain('--cert-identity "$signer"');
expect(raw).toContain('--source-digest "$source_commit"');
expect(raw).toContain('--deny-self-hosted-runners');
expect(raw).toContain('Recheck public visibility and anonymous pulls for every qualified digest');
expect(raw).toContain('scripts/cso-public-ghcr.ts verify');
expect(raw).toContain('public-image-evidence');
expect(raw).toContain('--remove-after');
expect(raw).toContain('scripts/cso-runtime-promotion.ts');
expect(raw).toContain('name: cso-qualified-runtime-statements');
expect(raw).not.toContain('contents: write');
for (const step of job.steps) if (step.uses) expect(step.uses).toMatch(/@[a-f0-9]{40}$/);
});
test('staged images enter the daemon only after signed evidence checks and before no-pull execution', () => {
const workflow = Bun.YAML.parse(readFileSync(join(ROOT, '.github/workflows/cso-runtime-images.yml'), 'utf8')) as any;
const steps = workflow.jobs.stage.steps;
const verified = steps.findIndex((s: any) => s.run?.includes('--predicate-type https://spdx.dev/Document/v2.3'));
const pulled = steps.findIndex((s: any) => s.run?.includes('scripts/cso-public-ghcr.ts verify'));
const executed = steps.findIndex((s: any) => s.run?.includes('test/cso-docker-integration.test.ts'));
expect(verified).toBeGreaterThanOrEqual(0);
expect(pulled).toBeGreaterThan(verified);
expect(executed).toBeGreaterThan(pulled);
expect(steps[pulled].env.CSO_IMAGE).toBe('${{ steps.image.outputs.name }}@${{ steps.build.outputs.digest }}');
expect(steps[pulled].env.GH_TOKEN).toBe('${{ github.token }}');
expect(steps[pulled].run).toContain('--image "$CSO_IMAGE" --platform "$CSO_PLATFORM"');
expect(steps[pulled].run).toContain('--repository "$GITHUB_REPOSITORY" --output public-image.json');
expect(steps[pulled]['continue-on-error']).not.toBe(true);
const qualify=workflow.jobs['qualify-native'];
const loaded=qualify.steps.findIndex((s:any)=>s.run?.includes('GSTACK_CSO_TEST_IMAGE'));
const cold=qualify.steps.findIndex((s:any)=>s.run?.includes('bun run test:cso:docker'));
expect(loaded).toBeGreaterThanOrEqual(0);expect(cold).toBeGreaterThan(loaded);
expect(qualify.needs).toEqual(['reviewed-inputs','stage']);
expect(qualify.steps.some((s:any)=>s.uses?.startsWith('docker/login-action@'))).toBe(false);
expect(qualify.steps[loaded].run).toContain('scripts/cso-public-ghcr.ts verify');
expect(qualify.steps[loaded].env.GH_TOKEN).toBe('${{ github.token }}');
});
});
+72
View File
@@ -0,0 +1,72 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';import * as os from 'node:os';import * as path from 'node:path';import { spawn, spawnSync } from 'node:child_process';
import { DockerGroup, dockerEndpoint, dockerProbe } from '../lib/cso/docker';import { admit, release } from '../lib/cso/admission';
const enabled=process.env.GSTACK_CSO_DOCKER_TESTS==='1',suite=enabled?describe:describe.skip;let root='',state='',image='',volumeImage='',endpoint:any,watchdog='';
function exec(file:string,args:string[],env:Record<string,string>={}){const r=spawnSync(file,args,{encoding:'utf8',env:{PATH:'/usr/local/bin:/usr/bin:/bin',HOME:root,GSTACK_HOME:state,DOCKER_HOST:'unix:///var/run/docker.sock',...env},timeout:120_000});if(r.status!==0)throw new Error(`${file} failed: ${r.stderr}`);return r.stdout.trim();}
beforeAll(()=>{if(!enabled)return;root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-docker-'));state=path.join(root,'state');process.env.GSTACK_HOME=state;watchdog=path.resolve(import.meta.dir,'../bin/gstack-cso-watchdog');if(!fs.existsSync(watchdog))throw new Error('GSTACK_CSO_DOCKER_TESTS=1 requires the compiled watchdog');
const source=`#include <arpa/inet.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\nstatic int blocked4(const char *ip,int port){int s=socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in a={.sin_family=AF_INET,.sin_port=htons(port)};inet_pton(AF_INET,ip,&a.sin_addr);int r=connect(s,(void*)&a,sizeof a);close(s);return r!=0;}\nstatic int blocked6(void){int s=socket(AF_INET6,SOCK_STREAM,0);struct sockaddr_in6 a={.sin6_family=AF_INET6,.sin6_port=htons(443)};inet_pton(AF_INET6,"2606:4700:4700::1111",&a.sin6_addr);int r=connect(s,(void*)&a,sizeof a);close(s);return r!=0;}\nint main(int argc,char**argv){if(strstr(argv[0],"entrypoint")){if(argc<2||argv[1][0]!='/')return 64;execv(argv[1],argv+1);return 69;}if(strstr(argv[0],"sleep")){for(;;)sleep(3600);}if(strstr(argv[0],"reader")){char a[16]={0},b[16]={0};FILE*x=fopen("/source/source.txt","r"),*y=fopen("/policy/verification.json","r");if(!x||!y||!fgets(a,sizeof a,x)||!fgets(b,sizeof b,y))return 12;fclose(x);fclose(y);if(strcmp(a,"source\\n")||strcmp(b,"policy\\n"))return 13;puts("INPUTS_OK");return 0;}if(strstr(argv[0],"server")){int s=socket(AF_INET,SOCK_STREAM,0),c;struct sockaddr_in a={.sin_family=AF_INET,.sin_port=htons(34567),.sin_addr.s_addr=htonl(INADDR_LOOPBACK)};int one=1;setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&one,sizeof one);if(bind(s,(void*)&a,sizeof a)||listen(s,2))return 2;for(;;){c=accept(s,0,0);if(c>=0){write(c,"ok",2);close(c);}}}int fd=open("/should-not-write",O_WRONLY|O_CREAT,0600);if(fd>=0)return 10;FILE*f=fopen("/proc/self/status","r");char line[256];int caps=1,nnp=0;while(f&&fgets(line,sizeof line,f)){if(!strncmp(line,"CapEff:",7))caps=strtoull(line+7,0,16)!=0;if(!strncmp(line,"NoNewPrivs:",11))nnp=atoi(line+11);}if(f)fclose(f);int s=socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in a={.sin_family=AF_INET,.sin_port=htons(34567),.sin_addr.s_addr=htonl(INADDR_LOOPBACK)};int loop=connect(s,(void*)&a,sizeof a)==0;close(s);struct addrinfo*h=0;int dns=getaddrinfo("example.com",0,0,&h);if(h)freeaddrinfo(h);if(getuid()==0||caps||!nnp||!loop||!blocked4("8.8.8.8",443)||!blocked4("169.254.169.254",80)||!blocked6()||dns==0)return 11;puts("CONTAINMENT_OK");return 0;}\n`;
fs.writeFileSync(path.join(root,'probe.c'),source);exec('/usr/bin/cc',['-static','-O2',path.join(root,'probe.c'),'-o',path.join(root,'probe')]);exec('/usr/bin/cc',['-static','-O2',path.join(import.meta.dir,'fixtures/cso-http-smoke.c'),'-o',path.join(root,'http-server')]);fs.writeFileSync(path.join(root,'Dockerfile'),'FROM scratch\nCOPY probe /opt/cso/entrypoint\nCOPY probe /bin/sleep\nCOPY probe /server\nCOPY probe /client\nCOPY probe /reader\nCOPY probe /policy/.keep\nCOPY probe /source/.keep\nCOPY http-server /http-server\nUSER 10001:10001\nENTRYPOINT ["/opt/cso/entrypoint"]\n');const tag=`gstack-cso-fixture:${process.pid}`;exec('/usr/bin/docker',['--host','unix:///var/run/docker.sock','build','--network=none','--tag',tag,root]);image=exec('/usr/bin/docker',['--host','unix:///var/run/docker.sock','image','inspect','--format','{{.Id}}',tag]);fs.writeFileSync(path.join(root,'VolumeDockerfile'),`FROM ${tag}\nVOLUME ["/unbounded"]\n`);const volumeTag=`gstack-cso-volume-fixture:${process.pid}`;exec('/usr/bin/docker',['--host','unix:///var/run/docker.sock','build','--network=none','--file',path.join(root,'VolumeDockerfile'),'--tag',volumeTag,root]);volumeImage=exec('/usr/bin/docker',['--host','unix:///var/run/docker.sock','image','inspect','--format','{{.Id}}',volumeTag]);},300_000);
afterAll(()=>{if(root){for(const candidate of [volumeImage,image])try{if(candidate)spawnSync('/usr/bin/docker',['--host','unix:///var/run/docker.sock','image','rm','--force',candidate],{stdio:'ignore',timeout:30_000});}catch{}fs.rmSync(root,{recursive:true,force:true});}delete process.env.GSTACK_HOME;},120_000);
suite('CSO Docker containment integration',()=>{
test('hard fails when local daemon enforcement prerequisites are absent',async()=>{endpoint=await dockerEndpoint(root,{HOME:root,DOCKER_HOST:'unix:///var/run/docker.sock'});const info=await dockerProbe(endpoint,root);expect(info.security.some((x:string)=>x.includes('seccomp'))).toBe(true);});
test('rejects image-declared writable volumes before container creation',async()=>{const dir=path.join(root,'volume-rejection');fs.mkdirSync(dir);const group=await DockerGroup.create(endpoint,`volume-${Date.now()}`,dir,Date.now()+60_000,image,watchdog);try{const before=exec('/usr/bin/docker',['--host',endpoint.uri,'volume','ls','--quiet']);await expect(group.createContainer({role:'app',image:volumeImage,command:['/bin/sleep','1']})).rejects.toThrow('declares writable volumes');expect(exec('/usr/bin/docker',['--host',endpoint.uri,'volume','ls','--quiet'])).toBe(before);}finally{await group.cleanup();}},120_000);
test('shares only loopback while denying egress, privileges, and daemon logs, and reads private source/policy mounts',async()=>{const dir=path.join(root,'group');fs.mkdirSync(dir);const group=await DockerGroup.create(endpoint,`integration-${Date.now()}`,dir,Date.now()+60_000,image,watchdog);let server='',client='';try{server=await group.createContainer({role:'app',image,command:['/server']});await group.start(server);client=await group.createContainer({role:'verifier',image,command:['/client']});const result=await group.startAttach(client);expect(result).toEqual({code:0,output:'CONTAINMENT_OK\n'});const sourceDir=path.join(dir,'private-source'),policy=path.join(dir,'verification.json');fs.mkdirSync(sourceDir,{mode:0o700});fs.writeFileSync(path.join(sourceDir,'source.txt'),'source\n',{mode:0o600});fs.writeFileSync(policy,'policy\n',{mode:0o600});const reader=await group.createContainer({role:'browser',image,source:sourceDir,command:['/reader'],readonlyFiles:[{host:policy,container:'/policy/verification.json'}]});expect(await group.startAttach(reader)).toEqual({code:0,output:'INPUTS_OK\n'});const raw=exec('/usr/bin/docker',['--host',endpoint.uri,'inspect',client]),inspect=JSON.parse(raw)[0];expect(inspect.HostConfig).toMatchObject({ReadonlyRootfs:true,NetworkMode:`container:${group.anchor}`,PidsLimit:32,ShmSize:8*1024*1024,LogConfig:{Type:'none',Config:{}}});expect(inspect.Config.User).toBe(`${process.getuid?.()}:${process.getgid?.()}`);expect(inspect.HostConfig.CapDrop).toEqual(['ALL']);expect(inspect.HostConfig.SecurityOpt).toContain('no-new-privileges:true');expect(inspect.HostConfig.PortBindings).toEqual({});expect(inspect.Mounts.every((m:any)=>m.Destination!=='/var/run/docker.sock')).toBe(true);}finally{await group.cleanup();}expect(spawnSync('/usr/bin/docker',['--host',endpoint.uri,'inspect',client],{timeout:30_000}).status).not.toBe(0);});
test('machine-wide admission allows only two groups per endpoint',()=>{const a=admit(endpoint.uri,'a',Date.now()+60_000),b=admit(endpoint.uri,'b',Date.now()+60_000);try{expect(()=>admit(endpoint.uri,'c',Date.now()+60_000)).toThrow('Two reproduction groups');}finally{release(a);release(b);}});
test('staged runtime executes its trusted verifier and checks every declared tool version', async () => {
const staged = process.env.GSTACK_CSO_TEST_IMAGE;
if (!staged) return;
expect(staged).toMatch(/@sha256:[a-f0-9]{64}$/);
expect(process.env.GSTACK_CSO_TEST_PLATFORM).toBe(process.arch === 'arm64' ? 'linux/arm64' : 'linux/amd64');
const versions = JSON.parse(process.env.GSTACK_CSO_EXPECTED_VERSIONS || '{}');
const commands: Record<string, Record<string, string[]>> = {
node: {node: ['/usr/local/bin/node', '--version'], npm: ['/usr/local/bin/npm', '--version'], 'cso-preparation': ['/opt/cso/preparation', '--version']},
bun: {bun: ['/usr/local/bin/bun', '--version'], 'cso-preparation': ['/opt/cso/preparation', '--version']},
python: {python: ['/usr/local/bin/python', '--version'], uv: ['/usr/local/bin/uv', '--version'], 'cso-preparation': ['/opt/cso/preparation', '--version']},
rails: {ruby: ['/usr/local/bin/ruby', '--version'], bundler: ['/usr/local/bin/bundle', '--version'], 'cso-preparation': ['/opt/cso/preparation', '--version']},
postgresql: {postgresql: ['/opt/cso/bin/postgres', '--version']},
};
const prefixes: Record<string, string> = {node: 'v', npm: '', bun: '', python: 'Python ', uv: 'uv ', ruby: 'ruby ', bundler: 'Bundler version ', postgresql: 'postgres (PostgreSQL) ', 'cso-preparation': ''};
const stack = commands[process.env.GSTACK_CSO_TEST_STACK || ''];
expect(stack).toBeDefined();
expect(Object.keys(versions).sort()).toEqual(Object.keys(stack).sort());
const dir = path.join(root, 'staged'); fs.mkdirSync(dir, {mode: 0o700});
const policy = {
phase: 'after', port: 34568,
legitimate: [{name: 'available feature', path: '/control', method: 'GET', expected: {status: 200, includes: 'CONTROL_OK'}}],
security: {name: 'denied access', path: '/security', method: 'GET', expected: {status: 403, includes: 'DENIED'}, vulnerable: {status: 200, includes: 'SECRET'}},
};
const positivePath = path.join(dir, 'positive.json'), negativePath = path.join(dir, 'broken-control.json');
fs.writeFileSync(positivePath, JSON.stringify(policy), {mode: 0o600});
fs.writeFileSync(negativePath, JSON.stringify({...policy, legitimate: [{...policy.legitimate[0], expected: {status: 201, includes: 'CONTROL_OK'}}]}), {mode: 0o600});
const group = await DockerGroup.create(endpoint, `staged-${Date.now()}`, dir, Date.now() + 90_000, staged, watchdog);
try {
for (const [tool, version] of Object.entries(versions)) {
const result = await group.execAttach(group.anchor, stack[tool]);
expect(result.code).toBe(0);
const expected = `${prefixes[tool]}${version}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
expect(result.output.trim()).toMatch(new RegExp(`^${expected}(?:$|\\s)`));
}
if(process.env.GSTACK_CSO_TEST_STACK==='postgresql'){
const databases=path.join(dir,'postgresql.databases');fs.writeFileSync(databases,'cso_primary\ncso_queue\n',{mode:0o444});
const postgres=await group.createContainer({role:'postgres',image:staged,command:['/opt/cso/run-postgresql','/policy/postgresql.databases'],postgresDatabasePolicy:databases});await group.start(postgres);
let ready=false;for(let attempt=0;attempt<100&&!ready;attempt++){const checked=await group.execAttach(postgres,['/opt/cso/postgresql-ready','/policy/postgresql.databases']);ready=checked.code===0;if(!ready)await Bun.sleep(50);}expect(ready).toBe(true);
const raw=exec('/usr/bin/docker',['--host',endpoint.uri,'inspect',postgres]),inspect=JSON.parse(raw)[0];expect(inspect.Config.User).toBe('10001:10001');
await group.removeContainer(postgres);
}
const server = await group.createContainer({role: 'app', image, command: ['/http-server']});
await group.start(server);
const verifier = await group.createContainer({
role: 'verifier', image: staged, command: ['/bin/sleep', '2147483647'],
readonlyFiles: [{host: positivePath, container: '/policy/positive.json'}, {host: negativePath, container: '/policy/broken-control.json'}],
});
await group.start(verifier);
const positive = await group.execAttach(verifier, ['/opt/cso/verifier', '/policy/positive.json']);
expect(positive.code).toBe(0);
expect(JSON.parse(positive.output)).toMatchObject({booted: true, legitimate: true, security: 'pass'});
const negative = await group.execAttach(verifier, ['/opt/cso/verifier', '/policy/broken-control.json']);
expect(negative.code).toBe(0);
expect(JSON.parse(negative.output)).toMatchObject({booted: true, legitimate: false, security: 'pass'});
} finally { await group.cleanup(); }
}, 120_000);
});
+29
View File
@@ -0,0 +1,29 @@
import { describe, expect, test } from 'bun:test';
import { GROUP_LIMITS, ROLE_LIMITS, type Role } from '../lib/cso/admission';
import { CONTAINER_SHM_BYTES, writableAllocation } from '../lib/cso/docker';
describe('CSO Docker writable-storage policy',()=>{
test('every default role includes the explicitly bounded shm allocation',()=>{
for(const role of Object.keys(ROLE_LIMITS) as Role[]){
const allocation=writableAllocation(role);
expect(allocation.shmBytes).toBe(CONTAINER_SHM_BYTES);
expect(allocation.totalBytes).toBe(ROLE_LIMITS[role].writableMiB*1024*1024);
expect(allocation.temporaryBytes).toBeGreaterThan(0);
expect(allocation.workBytes).toBeGreaterThan(0);
}
});
test('the Rails PostgreSQL group stays within two GiB including every shm mount',()=>{
const roles:Role[]=['anchor','postgres','app','verifier'];
const bytes=roles.reduce((sum,role)=>sum+writableAllocation(role).totalBytes,0);
expect(bytes).toBe(GROUP_LIMITS.writableMiB*1024*1024);
});
test('dependency acquisition accounts for metadata, archives, and shm together',()=>{
const mib=1024*1024,allocation=writableAllocation('app',{
temporaryTmpfsBytes:64*mib,workTmpfsBytes:64*mib,metadataTmpfsBytes:1024*mib,archiveTmpfsBytes:384*mib,
});
expect(allocation.totalBytes).toBe((64+64+1024+384)*mib+CONTAINER_SHM_BYTES);
expect(writableAllocation('anchor').totalBytes+allocation.totalBytes).toBeLessThan(GROUP_LIMITS.writableMiB*mib);
});
});
+812
View File
@@ -0,0 +1,812 @@
import { afterEach, describe, expect, test } from 'bun:test';
import { chmodSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { collectProducerReceipts, createEvalMatrix, createPortableSkillPayload, loadPortableSkillPayload, prepareEvalJobs, REQUIRED_CONTAINMENT, scoreCollectedEval, scoreEval, validateMatrix, validatePortableSkillPayload, type EvalCell, type EvalResult, type PreparedEvalSchedule } from '../scripts/cso-eval';
import { PRODUCER_PROVIDER_POLICY, producerFailureMessage, producerInstallationIdentity, resolveProducerHelperBinding, runProducerCell, validateProductionProducerInstallation } from '../scripts/cso-eval-producer';
import { producerArtifactInventoryHash, producerInstallationIdentityHash, producerProviderIdentityHash, producerReceiptHash, sha256, type ProducerArtifactInventory, type ProducerInstallationIdentity, type ProducerProviderIdentity, type ProducerReceipt } from '../scripts/cso-eval-protocol';
import type { Family, ProviderAdapter, RunOpts, RunResult } from './helpers/providers/types';
import { CORPUS_VERSION, FAMILIES, STACKS, loadCorpusManifest, materializeCase, sourceFiles, sourceHash } from './fixtures/cso-eval/materialize';
import { judgeRepair, oracleFor, type PrivateEvidence } from './helpers/cso-eval-oracles';
import { inspectPreparation } from '../lib/cso/preparation';
import { assertRuntimeCompatible, RUNTIME_CATALOG } from '../lib/cso/runtime-catalog';
import { canonicalStartPlan, canonicalTestPlan } from '../lib/cso/verification';
import { CsoError } from '../lib/cso/contracts';
import { geminiProducerPaths, geminiProducerSystemSettings } from './helpers/providers/gemini';
const temporary: string[] = [];
const root = () => { const path = mkdtempSync(join(tmpdir(), 'cso-eval-')); temporary.push(path); return path; };
function unlockTemporary(path: string): void {
if (!existsSync(path)) return;
const stat = lstatSync(path);
if (stat.isSymbolicLink()) return;
if (!stat.isDirectory()) { try { chmodSync(path, 0o600); } catch {} return; }
try { chmodSync(path, 0o700); } catch {}
for (const entry of readdirSync(path)) unlockTemporary(join(path, entry));
}
afterEach(() => { for (const path of temporary.splice(0)) { unlockTemporary(path); rmSync(path, { recursive: true, force: true }); } });
const corpus = loadCorpusManifest();
const matrix = createEvalMatrix({ model: 'matched-test-model', host: 'codex', skillHashes: { v2: 'a'.repeat(64), v3: 'b'.repeat(64) } });
const qualification = { containment: Object.fromEntries(REQUIRED_CONTAINMENT.map(name => [name, 'passed' as const])) };
function portableSkill(version: 'v2' | 'v3', sectionMarker: string): string {
const manifest = JSON.stringify({ $schema: 'https://gstack.dev/schemas/section-manifest.json', skill: 'cso', version: 1, sections: [{ id: 'audit-phases', file: 'audit-phases.md', title: 'Audit phases', trigger: 'during audit phases' }] }, null, 2) + '\n';
return createPortableSkillPayload(version, [
{ path: 'SKILL.md', contents: `---\nname: cso\nversion: ${version.slice(1)}.0.0\n---\n# CSO ${version}\nRead cso/sections/audit-phases.md before auditing.\n` },
{ path: 'sections/manifest.json', contents: manifest },
{ path: 'sections/audit-phases.md', contents: `# Complete ${version} phases\n${sectionMarker}\n` },
]);
}
/** Synthetic accounting records exercise the scorer. These are not measured agent results. */
function syntheticResult(cell: EvalCell): EvalResult {
const runtime = cell.mode === 'comprehensive';
const positive = cell.variant === 'vulnerable';
const repairEvidenceHash = 'c'.repeat(64), recheckEvidenceHash = 'd'.repeat(64);
return { cellId: cell.id, sourceHash: cell.sourceHash, skillHash: cell.skillHash, model: cell.model, host: cell.host, budgetSeconds: cell.budgetSeconds,
reportPresent: true, reportComplete: true,
findings: positive ? [{ id: 'finding-1', evidence: cell.version === 'v2' ? 'legacy_review' : 'supported', claimedTested: runtime && cell.version === 'v3', judgment: 'correct', matchedCaseId: cell.caseId,
...(runtime && cell.version === 'v3' ? { trustedVerification: { repair: 'passed' as const, repairEvidenceHash, recheck: 'passed' as const, recheckEvidenceHash } } : {}) }] : [],
setup: runtime ? 'passed' : 'not_attempted', reproduction: runtime && positive ? 'passed' : 'not_attempted', repair: runtime && positive ? 'passed' : 'not_attempted', recheck: runtime && positive ? 'passed' : 'not_attempted',
...(runtime && positive ? { oracleEvidenceHash: repairEvidenceHash, oracleVersion: CORPUS_VERSION, currentSourceHash: corpus.cases.find(fixture => fixture.id === cell.caseId)!.filesHash.fixed, recheckEvidenceHash } : {}),
heldOutAssertionsPassed: runtime && positive, freshRecheck: runtime && positive, latencyMs: 1500, firstUsefulResultMs: positive ? 500 : null,
};
}
const group = (score: ReturnType<typeof scoreEval>, version: string, mode: string) => score.groups.find(item => item.version === version && item.mode === mode)!;
function syntheticInstallationIdentity(seed = '1'): ProducerInstallationIdentity {
const artifact = (offset: number) => ({ sha256: sha256(`${seed}:artifact:${offset}`), bytes: 100 + offset });
const core = artifact(3);
const withoutHash: Omit<ProducerInstallationIdentity, 'identityHash'> = {
schemaVersion: 1,
producer: artifact(1), launcher: artifact(2), core, watchdog: artifact(4),
generation: { coreSha256: core.sha256, manifest: { sha256: sha256(`${core.sha256}\n`), bytes: 65 } },
embeddedCatalogs: {
runtimeRevision: 'runtime-test', runtimeBuildRevision: 'runtime-build-test', runtimeSha256: sha256(`${seed}:runtime`),
scannerRevision: 'scanner-test', scannerSha256: sha256(`${seed}:scanner`),
},
};
return { ...withoutHash, identityHash: producerInstallationIdentityHash(withoutHash) };
}
function syntheticProviderIdentity(family: Family = 'gpt', seed = '1'): ProducerProviderIdentity {
const withoutHash: Omit<ProducerProviderIdentity, 'identityHash'> = {
schemaVersion: 1, family, policyRevision: `${family}-test-policy`,
executable: { sha256: sha256(`${seed}:${family}:provider`), bytes: 200 }, argsPrefix: [], version: `${family}-test-version`,
};
return { ...withoutHash, identityHash: producerProviderIdentityHash(withoutHash) };
}
function syntheticArtifactInventory(): ProducerArtifactInventory {
const base = { schemaVersion: 1 as const, root: 'security/cso' as const, entries: [], totalBytes: 0 };
return { ...base, identityHash: producerArtifactInventoryHash(base) };
}
function syntheticReceipt(cell: EvalCell): ProducerReceipt {
const withoutHash: Omit<ProducerReceipt, 'receiptHash'> = { schemaVersion: 1, cell, inputHash: 'e'.repeat(64), installationIdentity: syntheticInstallationIdentity(), providerIdentity: syntheticProviderIdentity(cell.host === 'codex' ? 'gpt' : cell.host), artifacts: syntheticArtifactInventory(), startedAt: '2026-01-01T00:00:00.000Z', finishedAt: '2026-01-01T00:00:01.000Z', status: 'succeeded', requestedModel: cell.model, modelUsed: `resolved-${cell.model}`, modelIdentitySource: 'provider_reported', durationMs: 1000, firstUsefulResultMs: null, toolCalls: 1, output: 'synthetic producer transcript', outputHash: sha256('synthetic producer transcript'), usage: { inputTokens: 10, outputTokens: 5, cachedTokens: 0, estimatedCostUSD: 0.001 } };
return { ...withoutHash, receiptHash: producerReceiptHash(withoutHash) };
}
function syntheticSchedule(): PreparedEvalSchedule {
return { schemaVersion: 1, matrixHash: createHash('sha256').update(JSON.stringify(matrix)).digest('hex'), scheduledCells: matrix.cells.length, preparedCells: matrix.cells.length, jobs: matrix.cells.map(cell => ({ cellId: cell.id, relativePath: `jobs/${cell.id}`, inputHash: 'e'.repeat(64) })) };
}
function rehashReceipt(receipt: ProducerReceipt): ProducerReceipt {
const { receiptHash: _receiptHash, ...withoutHash } = receipt;
return { ...withoutHash, receiptHash: producerReceiptHash(withoutHash) };
}
function syntheticBatch() {
return collectProducerReceipts(matrix, syntheticSchedule(), matrix.cells.map(syntheticReceipt));
}
describe('CSO immutable evaluation corpus', () => {
test('contains forty distinct vulnerable/fixed pairs across ten families and four stacks', () => {
expect(corpus.cases).toHaveLength(40);
for (const stack of STACKS) expect(corpus.cases.filter(fixture => fixture.stack === stack).map(fixture => fixture.family)).toEqual([...FAMILIES]);
for (const fixture of corpus.cases) {
expect(fixture.filesHash.vulnerable).not.toBe(fixture.filesHash.fixed);
for (const variant of ['vulnerable', 'fixed'] as const) expect(sourceHash(sourceFiles(fixture.id, variant))).toBe(fixture.filesHash[variant]);
}
});
test('producer snapshots contain app source but no private oracle, labels, or alternative fixed copy', () => {
const parent = root(); const result = materializeCase('node-sql-injection', 'vulnerable', join(parent, 'app'));
expect(result.sourceHash).toBe(corpus.cases[0].filesHash.vulnerable);
expect(readdirSync(result.path).sort()).toEqual(['README.md', 'app.mjs', 'package-lock.json', 'package.json', 'test']);
const combined = ['README.md', 'app.mjs', 'package-lock.json', 'package.json', 'test/control.test.mjs']
.map(file => readFileSync(join(result.path, file), 'utf8')).join('\n');
expect(combined).not.toContain('ORACLE_SQL_MARKER'); expect(combined).not.toContain('filesHash'); expect(combined).not.toContain('heldOut');
expect(combined).toContain('the intended member workflow remains available');
expect(() => materializeCase('node-sql-injection', 'fixed', result.path)).toThrow('CORPUS_DESTINATION_EXISTS');
});
test('rejects unknown cases and symlink destination ancestors', () => {
const parent = root(); symlinkSync(parent, join(parent, 'alias'));
expect(() => materializeCase('unknown', 'fixed', join(parent, 'no'))).toThrow('UNKNOWN_CORPUS_CASE');
expect(() => materializeCase('node-sql-injection', 'fixed', join(parent, 'alias', 'no'))).toThrow('UNSAFE_CORPUS_DESTINATION');
});
test('all source fixtures have supported inert dependency metadata, including real Rails locks', () => {
const parent = root();
for (const fixture of corpus.cases) {
const { path } = materializeCase(fixture.id, 'vulnerable', join(parent, fixture.id));
const plan = inspectPreparation(path, fixture.stack);
expect({ id: fixture.id, status: plan.status, prerequisites: plan.prerequisites }).toEqual({ id: fixture.id, status: 'ready', prerequisites: [] });
const reviewed = RUNTIME_CATALOG.profiles.find(profile => profile.stack === fixture.stack && profile.platform === 'linux/amd64');
expect(reviewed).toBeDefined();
expect(() => assertRuntimeCompatible(plan, reviewed as any)).not.toThrow();
}
});
test('every pair exposes a helper-derived startup and nonempty legitimate regression suite', () => {
const parent = root();
for (const fixture of corpus.cases) for (const variant of ['vulnerable', 'fixed'] as const) {
const { path } = materializeCase(fixture.id, variant, join(parent, `${fixture.id}-${variant}`));
const start = canonicalStartPlan(path, fixture.stack, 8000);
const tests = canonicalTestPlan(path, fixture.stack);
expect(start.command.executable.startsWith('/')).toBe(true);
expect(start.entrypointFiles.length).toBeGreaterThan(0);
expect(tests.commands.length).toBeGreaterThan(0);
expect(tests.minimumPassingTests.every(count => count >= 1)).toBe(true);
expect(tests.files.some(file => /(?:test|spec)/.test(file))).toBe(true);
}
});
test('Rails lock provenance binds the native resolver inputs and exact public archive metadata', () => {
const source = sourceFiles('rails-sql-injection', 'vulnerable');
const provenance = JSON.parse(readFileSync(new URL('./fixtures/cso-eval/rails-lock-provenance.json', import.meta.url), 'utf8'));
const hash = (value: string) => createHash('sha256').update(value).digest('hex');
expect(hash(source.Gemfile)).toBe(provenance.gemfileSha256); expect(hash(source['Gemfile.lock'])).toBe(provenance.lockSha256);
const pins = [...source['Gemfile.lock'].matchAll(/^ ([A-Za-z0-9_.-]+) \(([^)]+)\)$/gm)];
expect(provenance.archives).toHaveLength(pins.length);
for (const [, name, version] of pins) expect(provenance.archives.some((archive: any) => archive.name === name && archive.version === version && /^[a-f0-9]{64}$/.test(archive.sha256))).toBe(true);
expect(provenance.qualification).toContain('not-run');
});
});
describe('CSO private repair oracles', () => {
function validEvidence(): PrivateEvidence {
const oracle = oracleFor('object-authorization');
const control = { request: oracle.controls[0].request, response: { status: 200, body: '{"owner":"member-1","total":12}' } };
return { original: { booted: true, controls: [control], attack: { request: oracle.attack, response: { status: 200, body: '{"owner":"member-2","total":9800}' } } },
patched: { booted: true, controls: [control], attack: { request: oracle.attack, response: { status: 403, body: '{"error":"forbidden"}' } }, heldOut: oracle.heldOut.map(assertion => ({ request: assertion.request, response: { status: 403, body: '{"error":"forbidden"}' } })), existingTestsPassed: true },
immutableVerifier: true, independentRootCauseReview: true, featurePreserved: true, boundaryMocks: false };
}
test('requires original exploit, unchanged verifier, legitimate behavior, and held-out assertions', () => {
const evidence = validEvidence(); expect(judgeRepair('object-authorization', evidence).correctRepair).toBe(true);
for (const mutation of [(item: PrivateEvidence) => { item.original.booted = false; }, (item: PrivateEvidence) => { item.patched.controls = []; }, (item: PrivateEvidence) => { item.patched.heldOut = []; }, (item: PrivateEvidence) => { item.immutableVerifier = false; }, (item: PrivateEvidence) => { item.featurePreserved = false; }, (item: PrivateEvidence) => { item.boundaryMocks = true; }]) {
const changed = structuredClone(evidence); mutation(changed); expect(judgeRepair('object-authorization', changed).correctRepair).toBe(false);
}
});
test('a setup error cannot become an intended security assertion failure', () => {
const evidence = validEvidence(); evidence.original.attack.response = { status: 500, body: 'database unavailable' };
expect(judgeRepair('object-authorization', evidence).reproduced).toBe(false);
});
test('every family has a legitimate control and at least one withheld alternate assertion', () => {
for (const family of FAMILIES) { const oracle = oracleFor(family); expect(oracle.controls.length).toBeGreaterThan(0); expect(oracle.heldOut.length).toBeGreaterThan(0); }
});
});
describe('CSO matched evaluation accounting', () => {
test('pins three repetitions and identical models, sources, budgets, and host for v2/v3', () => {
expect(matrix.cells).toHaveLength(960); expect(matrix.repetitions).toBe(3); expect(new Set(matrix.cells.map(cell => cell.id)).size).toBe(960);
validateMatrix(matrix);
const changed = structuredClone(matrix); changed.cells[1].budgetSeconds += 1; expect(() => validateMatrix(changed)).toThrow('UNMATCHED_OR_INCOMPLETE');
expect(() => createEvalMatrix({ model: 'model', host: 'codex', skillHashes: { v2: 'a'.repeat(64), v3: 'a'.repeat(64) } })).toThrow('INVALID_MATCHED_EVAL_INPUT');
});
test('empty results are unmeasured, with honest denominators and unknown cost', () => {
const score = scoreEval(matrix, []); expect(score.status).toBe('unmeasured'); expect(Object.values(score.gates).every(value => value === 'unmeasured')).toBe(true);
const comprehensive = group(score, 'v3', 'comprehensive'); expect(comprehensive.reproduction).toEqual({ numerator: 0, denominator: 120, value: 0 }); expect(comprehensive.precision.value).toBeNull(); expect(comprehensive.cost.totalUSD).toBeNull();
});
test('fully adjudicated synthetic results exercise all release gates without missing denominators', () => {
const score = scoreEval(matrix, matrix.cells.map(syntheticResult), qualification);
expect(score.status).toBe('qualified'); expect(Object.values(score.gates).every(value => value === 'pass')).toBe(true);
expect(group(score, 'v3', 'daily').precision).toEqual({ numerator: 120, denominator: 120, value: 1 });
expect(group(score, 'v3', 'comprehensive').highCriticalRecall).toEqual({ numerator: 96, denominator: 96, value: 1 });
expect(score.perStack.rails).toEqual({ correctHeldOutRepairs: 10, denominator: 10 });
});
test('incomplete mandatory reports cannot contribute security evidence or qualify a complete matrix', () => {
const results = matrix.cells.map(syntheticResult);
for (const result of results) result.reportComplete = false;
const score = scoreEval(matrix, results, qualification), daily = group(score, 'v3', 'daily'), comprehensive = group(score, 'v3', 'comprehensive');
expect(score.status).toBe('partial');
expect(score.gates.matchedCompleteMatrix).toBe('fail');
expect(score.gates.mandatoryReports).toBe('fail');
expect(daily.reports).toEqual({ numerator: 0, denominator: 240, value: 0 });
expect(daily.precision).toEqual({ numerator: 0, denominator: 0, value: null });
expect(daily.recall.numerator).toBe(0);
expect(comprehensive.setup.numerator).toBe(0);
expect(comprehensive.reproduction.numerator).toBe(0);
expect(comprehensive.repair.numerator).toBe(0);
expect(comprehensive.recheck.numerator).toBe(0);
expect(score.perStack.rails.correctHeldOutRepairs).toBe(0);
});
test('release scoring binds every trusted judgment to a complete matched producer batch', () => {
const batch = syntheticBatch();
const results = matrix.cells.map(cell => ({ ...syntheticResult(cell), producerReceiptHash: batch.receipts.find(receipt => receipt.cell.id === cell.id)!.receiptHash }));
expect(scoreCollectedEval(matrix, batch, results, qualification)).toMatchObject({ status: 'qualified', producerBatchHash: batch.batchHash });
delete results[0].producerReceiptHash;
expect(() => scoreCollectedEval(matrix, batch, results, qualification)).toThrow('UNBOUND_EVAL_RESULT');
const incomplete = structuredClone(batch); incomplete.receipts.pop();
expect(() => scoreCollectedEval(matrix, incomplete, matrix.cells.map(syntheticResult), qualification)).toThrow('INCOMPLETE_PRODUCER_BATCH');
});
test('rejects tampered and mixed producer installation/provider identities across incomplete batches', () => {
const cells = matrix.cells.filter(cell => cell.version === 'v2').slice(0, 2);
const first = syntheticReceipt(cells[0]);
const tampered = structuredClone(first);
tampered.installationIdentity.core.sha256 = 'f'.repeat(64);
expect(() => collectProducerReceipts(matrix, syntheticSchedule(), [rehashReceipt(tampered)])).toThrow('INVALID_PRODUCER_GENERATION_IDENTITY');
const artifactTamper = structuredClone(first);
artifactTamper.artifacts.totalBytes = 1;
expect(() => collectProducerReceipts(matrix, syntheticSchedule(), [rehashReceipt(artifactTamper)])).toThrow('INVALID_PRODUCER_RECEIPT');
const mixedInstallation = syntheticReceipt(cells[1]);
mixedInstallation.installationIdentity = syntheticInstallationIdentity('different-installation');
expect(() => collectProducerReceipts(matrix, syntheticSchedule(), [first, rehashReceipt(mixedInstallation)])).toThrow('UNMATCHED_PRODUCER_INSTALLATIONS');
const mixedProvider = syntheticReceipt(cells[1]);
mixedProvider.providerIdentity = syntheticProviderIdentity('gpt', 'different-provider');
expect(() => collectProducerReceipts(matrix, syntheticSchedule(), [first, rehashReceipt(mixedProvider)])).toThrow('UNMATCHED_PRODUCER_PROVIDERS');
});
test('high/critical recall includes critical cases and detects a v3 regression',()=>{
const criticalCorpus=structuredClone(corpus);criticalCorpus.cases.find(item=>item.severity==='medium')!.severity='critical';
const criticalMatrix=createEvalMatrix({model:'matched-test-model',host:'codex',skillHashes:{v2:'a'.repeat(64),v3:'b'.repeat(64)}},criticalCorpus),complete=criticalMatrix.cells.map(syntheticResult);
expect(group(scoreEval(criticalMatrix,complete,qualification,criticalCorpus),'v3','comprehensive').highCriticalRecall).toEqual({numerator:99,denominator:99,value:1});
const criticalId=criticalCorpus.cases.find(item=>item.severity==='critical')!.id;
for(let index=0;index<criticalMatrix.cells.length;index++){const cell=criticalMatrix.cells[index];if(cell.version==='v3'&&cell.mode==='comprehensive'&&cell.variant==='vulnerable'&&cell.caseId===criticalId)complete[index].findings=[];}
const regressed=scoreEval(criticalMatrix,complete,qualification,criticalCorpus);expect(group(regressed,'v3','comprehensive').highCriticalRecall).toEqual({numerator:96,denominator:99,value:96/99});expect(regressed.gates.noHighCriticalRecallRegression).toBe('fail');
});
test('supported setup blocks count as misses, including unreproduced and unrepaired work', () => {
const cell = matrix.cells.find(item => item.version === 'v3' && item.mode === 'comprehensive' && item.variant === 'vulnerable')!;
const result = syntheticResult(cell); result.setup = 'blocked'; result.prerequisite = 'missing native library'; result.findings = []; result.reproduction = result.repair = result.recheck = 'blocked';
const score = scoreEval(matrix, [result]); const comprehensive = group(score, 'v3', 'comprehensive');
expect(comprehensive.setup).toEqual({ numerator: 0, denominator: 240, value: 0 }); expect(comprehensive.highCriticalRecall.denominator).toBe(96); expect(comprehensive.setupBlocked).toBe(1);
});
test('duplicate findings do not inflate precision and fixed-source false positives count', () => {
const positive = matrix.cells.find(item => item.version === 'v3' && item.mode === 'daily' && item.variant === 'vulnerable')!;
const negative = matrix.cells.find(item => item.version === 'v3' && item.mode === 'daily' && item.variant === 'fixed')!;
const one = syntheticResult(positive); one.findings.push({ ...one.findings[0], id: 'duplicate' });
const two = syntheticResult(negative); two.findings.push({ id: 'false-positive', evidence: 'supported', claimedTested: false, judgment: 'incorrect' });
expect(group(scoreEval(matrix, [one, two]), 'v3', 'daily').precision).toEqual({ numerator: 1, denominator: 3, value: 1 / 3 });
});
test('producer tested claims require independent successful held-out repair evidence', () => {
const cell = matrix.cells.find(item => item.version === 'v3' && item.mode === 'comprehensive' && item.variant === 'vulnerable')!;
const result = syntheticResult(cell); delete result.oracleEvidenceHash;
const score = scoreEval(matrix, [result]); expect(group(score, 'v3', 'comprehensive').falseTested).toBe(1); expect(group(score, 'v3', 'comprehensive').repair.numerator).toBe(0);
});
test('one trusted cell repair cannot certify another claimed-tested finding', () => {
const results = matrix.cells.map(syntheticResult), cell = matrix.cells.find(item => item.version === 'v3' && item.mode === 'comprehensive' && item.variant === 'vulnerable')!, result = results.find(item => item.cellId === cell.id)!;
result.findings.push({ id: 'unbound-tested-claim', evidence: 'supported', claimedTested: true, judgment: 'correct', matchedCaseId: cell.caseId });
const score = scoreEval(matrix, results, qualification);
expect(group(score, 'v3', 'comprehensive').falseTested).toBe(1); expect(score.gates.zeroFalselyTestedRepairs).toBe('fail'); expect(score.status).toBe('partial');
result.findings[1].trustedVerification = structuredClone(result.findings[0].trustedVerification);
expect(() => scoreEval(matrix, results, qualification)).toThrow('DUPLICATE_TRUSTED_REPAIR_BINDING');
});
test('per-stack held-out repair requires the matching supported discovery', () => {
const results = matrix.cells.map(syntheticResult);
for (let index = 0; index < matrix.cells.length; index++) {
const cell = matrix.cells[index];
if (cell.version === 'v3' && cell.mode === 'comprehensive' && cell.variant === 'vulnerable' && cell.stack === 'rails') results[index].findings = [];
}
const score = scoreEval(matrix, results, qualification);
expect(score.perStack.rails).toEqual({ correctHeldOutRepairs: 0, denominator: 10 });
expect(score.gates.heldOutRepairEachStack).toBe('fail');
});
test('legacy review evidence cannot raise v3 recall or earn tested repair', () => {
const cell = matrix.cells.find(item => item.version === 'v3' && item.mode === 'comprehensive' && item.variant === 'vulnerable')!;
const result = syntheticResult(cell); result.findings[0].evidence = 'legacy_review';
const score = scoreEval(matrix, [result]); expect(group(score, 'v3', 'comprehensive').recall.numerator).toBe(0); expect(group(score, 'v3', 'comprehensive').falseTested).toBe(1);
});
test('old-source recheck and missing held-out assertions cannot establish repair success', () => {
const cell = matrix.cells.find(item => item.version === 'v3' && item.mode === 'comprehensive' && item.variant === 'vulnerable')!;
const result = syntheticResult(cell); result.currentSourceHash = cell.sourceHash;
expect(group(scoreEval(matrix, [result]), 'v3', 'comprehensive').recheck.numerator).toBe(0);
result.heldOutAssertionsPassed = false; expect(group(scoreEval(matrix, [result]), 'v3', 'comprehensive').repair.numerator).toBe(0);
});
test('accepts a correct alternative patch but rejects reused repair evidence for closure', () => {
const cell = matrix.cells.find(item => item.version === 'v3' && item.mode === 'comprehensive' && item.variant === 'vulnerable')!;
const result = syntheticResult(cell); result.currentSourceHash = 'e'.repeat(64);
expect(group(scoreEval(matrix, [result]), 'v3', 'comprehensive').recheck.numerator).toBe(1);
result.recheckEvidenceHash = result.oracleEvidenceHash;
expect(group(scoreEval(matrix, [result]), 'v3', 'comprehensive').recheck.numerator).toBe(0);
});
test('rejects mismatched model budgets, duplicate results, and application execution in daily mode', () => {
const result = syntheticResult(matrix.cells[0]);
expect(() => scoreEval(matrix, [result, result])).toThrow('UNKNOWN_OR_DUPLICATE');
expect(() => scoreEval(matrix, [{ ...result, model: 'other-model' }])).toThrow('UNMATCHED_EVAL_RESULT');
expect(() => scoreEval(matrix, [{ ...result, setup: 'passed' }])).toThrow('DAILY_EVAL_EXECUTED');
const comprehensive = syntheticResult(matrix.cells.find(cell => cell.version === 'v3' && cell.mode === 'comprehensive' && cell.variant === 'vulnerable')!);
comprehensive.findings[0].trustedVerification = { repair: 'failed', repairEvidenceHash: 'not-a-hash', recheck: 'not_attempted' };
expect(() => scoreEval(matrix, [comprehensive])).toThrow('INVALID_TRUSTED_FINDING_VERIFICATION');
});
test('missing containment tests remain unmeasured and a failed canary fails qualification', () => {
const results = matrix.cells.map(syntheticResult);
expect(scoreEval(matrix, results).gates.containmentAndCanaries).toBe('unmeasured');
expect(scoreEval(matrix, results, { containment: { 'split-output-secrets': 'failed' } }).gates.containmentAndCanaries).toBe('fail');
});
test('precision and high-impact recall thresholds fail independently on a complete matrix', () => {
const results = matrix.cells.map(syntheticResult);
let falsePositives = 0, misses = 0;
for (let index = 0; index < matrix.cells.length; index++) {
const cell = matrix.cells[index];
if (cell.version === 'v3' && cell.mode === 'daily' && cell.variant === 'fixed' && falsePositives < 7) {
results[index].findings = [{ id: 'false-positive', evidence: 'supported', claimedTested: false, judgment: 'incorrect' }]; falsePositives++;
}
if (cell.version === 'v3' && cell.mode === 'comprehensive' && cell.variant === 'vulnerable' && corpus.cases.find(fixture => fixture.id === cell.caseId)!.severity === 'high' && misses < 20) {
results[index].findings = []; misses++;
}
}
const score = scoreEval(matrix, results, qualification);
expect(score.gates.dailyPrecision95).toBe('fail'); expect(score.gates.comprehensiveHighCriticalRecall80).toBe('fail'); expect(score.gates.noHighCriticalRecallRegression).toBe('fail');
});
});
describe('CSO matched producer orchestration', () => {
const skills = { v2: portableSkill('v2', 'V2_SECTION_ONLY'), v3: portableSkill('v3', 'V3_SECTION_ONLY') };
const producerMatrix = createEvalMatrix({ model: 'exact-eval-model', host: 'codex', skillHashes: { v2: sha256(skills.v2), v3: sha256(skills.v3) } });
const selected = producerMatrix.cells.filter(cell => cell.caseId === 'node-sql-injection' && cell.variant === 'vulnerable' && cell.mode === 'daily' && cell.repetition === 1);
const isolate = (prepared: string, cell: EvalCell) => {
const producerRoot = join(root(), 'producer'); mkdirSync(producerRoot);
const job = join(producerRoot, 'job'); cpSync(join(prepared, 'jobs', cell.id), job, { recursive: true });
return { job, input: join(job, 'producer-input.json'), source: join(job, 'source') };
};
const helperBundle = (directory = join(root(), 'helpers')) => {
mkdirSync(directory, { recursive: true });
const suffix = process.platform === 'win32' ? '.exe' : '';
for (const name of ['cso-eval-producer', 'gstack-cso-launcher', 'gstack-cso-core', 'gstack-cso-watchdog']) {
const path = join(directory, `${name}${suffix}`);
writeFileSync(path, process.platform === 'win32' ? 'test executable\n' : '#!/bin/sh\nexit 0\n', { mode: 0o755 });
if (process.platform !== 'win32') chmodSync(path, 0o755);
}
const core = join(directory, `gstack-cso-core${suffix}`);
writeFileSync(join(directory, '.gstack-cso-generation'), `${sha256(readFileSync(core))}\n`, { mode: 0o644 });
writeFileSync(join(directory, `provider-test${suffix}`), process.platform === 'win32' ? 'test provider\n' : '#!/bin/sh\nexit 0\n', { mode: 0o755 });
return join(directory, `gstack-cso-launcher${suffix}`);
};
const providerCommandFor = (launcher: string) => ({ executable: join(dirname(launcher), `provider-test${process.platform === 'win32' ? '.exe' : ''}`), argsPrefix: [] as string[] });
const providerIdentityFor = (family: Family, launcher: string): ProducerProviderIdentity => {
const command = providerCommandFor(launcher), contents = readFileSync(command.executable);
const withoutHash: Omit<ProducerProviderIdentity, 'identityHash'> = {
schemaVersion: 1,
family,
policyRevision: `${family}-test-policy`,
executable: { sha256: sha256(contents), bytes: contents.byteLength },
argsPrefix: [],
version: `${family}-test-version`,
};
return { ...withoutHash, identityHash: producerProviderIdentityHash(withoutHash) };
};
test('documents the complete five-artifact production installation', () => {
const guide = readFileSync(new URL('./fixtures/cso-eval/README.md', import.meta.url), 'utf8');
expect(guide).toContain('Trusted five-artifact producer unit');
expect(guide).toContain('bin/.gstack-cso-generation "$stage/.gstack-cso-generation"');
expect(guide).toContain('/opt/gstack-cso-producer/.gstack-cso-generation');
expect(guide).toContain('CSO_EVAL_PAID=1 /opt/gstack-cso-producer/cso-eval-producer run');
expect(guide).not.toMatch(/four-file|all four files|\/usr\/local\/bin\/cso-eval-producer/);
});
const withHelper = <T extends { paidExecutionAuthorized: boolean }>(options: T, launcher = helperBundle()) => ({
...options,
testHelperLauncherPath: launcher,
testProviderIdentity: providerIdentityFor((options as T & { adapter?: ProviderAdapter }).adapter?.family ?? 'gpt', launcher),
testProviderCommand: providerCommandFor(launcher),
});
class FakeAdapter implements ProviderAdapter {
readonly name = 'fake';
constructor(
private readonly inspect: (opts: RunOpts) => void,
private readonly actualModel = 'gpt-5.4',
readonly family: Family = 'gpt',
private readonly outcome: Partial<RunResult> = {},
) {}
async available() { return { ok: true }; }
async run(opts: RunOpts) {
this.inspect(opts);
return { output: 'complete — assessed source\nNo supported findings in the assessed scope', tokens: { input: 120, output: 30, cached: 10 }, durationMs: 1234, toolCalls: 4, modelUsed: this.actualModel, ...this.outcome };
}
estimateCost() { return 0.0042; }
}
test('pins reviewed provider CLI versions and policy identities', () => {
expect(PRODUCER_PROVIDER_POLICY).toEqual({
claude: { family: 'claude', policyRevision: 'claude-2.1.263-cso-v1', version: '2.1.263 (Claude Code)' },
codex: { family: 'gpt', policyRevision: 'codex-0.153.4-cso-v3-generation', version: 'codex-cli 0.153.4' },
gemini: { family: 'gemini', policyRevision: 'gemini-0.59.0-cso-v1', version: '0.59.0' },
});
});
test('prepares independent one-cell producer jobs without private or sibling source inputs', () => {
const destination = join(root(), 'prepared');
const schedule = prepareEvalJobs(producerMatrix, skills, destination, selected.map(cell => cell.id));
expect(schedule).toMatchObject({ scheduledCells: 960, preparedCells: 2 });
expect(readdirSync(join(destination, 'jobs')).sort()).toEqual(selected.map(cell => cell.id).sort());
for (const cell of selected) {
const source = join(destination, 'jobs', cell.id, 'source');
expect(existsSync(join(source, '.git'))).toBe(true);
const allSource = readdirSync(source).filter(name => name !== '.git').map(name => name).join('\n') + readFileSync(join(source, 'README.md'), 'utf8');
expect(allSource).not.toContain('ORACLE_SQL_MARKER');
expect(allSource).not.toContain('vulnerable');
expect(existsSync(join(destination, 'jobs', cell.id, 'producer-input.json'))).toBe(true);
const input = JSON.parse(readFileSync(join(destination, 'jobs', cell.id, 'producer-input.json'), 'utf8'));
expect(input.skill).toBe(skills[cell.version]);
expect(input.cell.skillHash).toBe(sha256(input.skill));
expect(input.skill).toContain(`${cell.version.toUpperCase()}_SECTION_ONLY`);
expect(input.skill).not.toContain(cell.version === 'v2' ? 'V3_SECTION_ONLY' : 'V2_SECTION_ONLY');
expect(validatePortableSkillPayload(input.skill, cell.version).files.map(file => file.path)).toEqual(['SKILL.md', 'sections/manifest.json', 'sections/audit-phases.md']);
}
expect(() => prepareEvalJobs(producerMatrix, { ...skills, v3: portableSkill('v3', 'CHANGED_V3_SECTION') }, join(root(), 'bad'), selected.map(cell => cell.id))).toThrow('EVAL_SKILL_HASH_MISMATCH');
});
test('binds every generated section byte and rejects incomplete or cross-version payloads', () => {
const changedSection = portableSkill('v3', 'V3_SECTION_CHANGED_BY_ONE_BYTE');
expect(sha256(changedSection)).not.toBe(sha256(skills.v3));
expect(validatePortableSkillPayload(skills.v2, 'v2').version).toBe('v2');
expect(validatePortableSkillPayload(skills.v3, 'v3').version).toBe('v3');
expect(() => validatePortableSkillPayload(skills.v2, 'v3')).toThrow('INVALID_CSO_EVAL_PAYLOAD');
expect(() => prepareEvalJobs(producerMatrix, { v2: '# root-only v2', v3: skills.v3 }, join(root(), 'root-only'), selected.map(cell => cell.id))).toThrow('INVALID_CSO_EVAL_PAYLOAD');
expect(() => createPortableSkillPayload('v3', [
{ path: 'SKILL.md', contents: '---\nname: cso\nversion: 3.0.0\n---\n' },
{ path: 'sections/manifest.json', contents: JSON.stringify({ skill: 'cso', version: 1, sections: [{ id: 'audit-phases', file: 'audit-phases.md', title: 'Audit phases', trigger: 'during audit phases' }] }) },
])).toThrow('INCOMPLETE_CSO_EVAL_PAYLOAD');
const directory = join(root(), 'skill'); mkdirSync(join(directory, 'sections'), { recursive: true });
writeFileSync(join(directory, 'SKILL.md'), '---\nname: cso\nversion: 3.0.0\n---\nRead sections/audit-phases.md.\n');
writeFileSync(join(directory, 'sections', 'manifest.json'), JSON.stringify({ skill: 'cso', version: 1, sections: [{ id: 'audit-phases', file: 'audit-phases.md', title: 'Audit phases', trigger: 'during audit phases' }] }));
writeFileSync(join(directory, 'sections', 'audit-phases.md'), 'COMPLETE_DIRECTORY_SECTION\n');
const loaded = loadPortableSkillPayload('v3', directory);
expect(loaded).toContain('COMPLETE_DIRECTORY_SECTION');
expect(() => loadPortableSkillPayload('v2', directory)).toThrow('CSO_EVAL_PAYLOAD_VERSION_MISMATCH');
writeFileSync(join(directory, 'sections', 'unlisted.md'), 'must not be omitted\n');
expect(() => loadPortableSkillPayload('v3', directory)).toThrow('UNLISTED_CSO_EVAL_SECTION');
});
test('binds only a complete adjacent executable helper bundle outside source and state', () => {
const source = join(root(), 'source'); mkdirSync(source);
const state = join(root(), 'state');
const launcher = helperBundle();
const suffix = process.platform === 'win32' ? '.exe' : '';
expect(resolveProducerHelperBinding(source, state, launcher)).toEqual({
producer: join(dirname(launcher), `cso-eval-producer${suffix}`),
launcher,
core: join(dirname(launcher), `gstack-cso-core${suffix}`),
watchdog: join(dirname(launcher), `gstack-cso-watchdog${suffix}`),
generation: join(dirname(launcher), '.gstack-cso-generation'),
});
const missingGeneration = helperBundle();
rmSync(join(dirname(missingGeneration), '.gstack-cso-generation'));
expect(() => resolveProducerHelperBinding(source, state, missingGeneration)).toThrow('INVALID_PRODUCER_HELPER');
const malformedGeneration = helperBundle();
writeFileSync(join(dirname(malformedGeneration), '.gstack-cso-generation'), 'f'.repeat(64));
expect(() => resolveProducerHelperBinding(source, state, malformedGeneration)).toThrow('INVALID_PRODUCER_HELPER');
const mismatchedGeneration = helperBundle();
writeFileSync(join(dirname(mismatchedGeneration), '.gstack-cso-generation'), `${'f'.repeat(64)}\n`);
const mismatchedBinding = resolveProducerHelperBinding(source, state, mismatchedGeneration);
expect(() => producerInstallationIdentity(mismatchedBinding)).toThrow('PRODUCER_HELPER_GENERATION_MISMATCH');
const missingWatchdog = helperBundle();
rmSync(join(dirname(missingWatchdog), `gstack-cso-watchdog${suffix}`));
expect(() => resolveProducerHelperBinding(source, state, missingWatchdog)).toThrow('INVALID_PRODUCER_HELPER');
const nonExecutable = helperBundle();
if (process.platform !== 'win32') {
chmodSync(join(dirname(nonExecutable), 'gstack-cso-core'), 0o600);
expect(() => resolveProducerHelperBinding(source, state, nonExecutable)).toThrow('INVALID_PRODUCER_HELPER');
}
const symlinked = helperBundle();
if (process.platform !== 'win32') {
rmSync(symlinked);
symlinkSync(join(dirname(symlinked), 'gstack-cso-core'), symlinked);
expect(() => resolveProducerHelperBinding(source, state, symlinked)).toThrow('INVALID_PRODUCER_HELPER');
}
const containedDirectory = join(source, 'helpers'); mkdirSync(containedDirectory);
const containedLauncher = join(containedDirectory, `gstack-cso-launcher${suffix}`);
for (const name of ['cso-eval-producer', 'gstack-cso-launcher', 'gstack-cso-core', 'gstack-cso-watchdog']) {
const path = join(containedDirectory, `${name}${suffix}`);
writeFileSync(path, 'test executable\n', { mode: 0o755 });
if (process.platform !== 'win32') chmodSync(path, 0o755);
}
expect(() => resolveProducerHelperBinding(source, state, containedLauncher)).toThrow('INVALID_PRODUCER_HELPER');
const stateContainedLauncher = helperBundle(join(state, 'helpers'));
expect(() => resolveProducerHelperBinding(source, state, stateContainedLauncher)).toThrow('INVALID_PRODUCER_HELPER');
});
test('production binding rejects a privileged or producer-writable binary and directory chain', () => {
const source = join(root(), 'source'); mkdirSync(source);
const state = join(root(), 'state');
const suffix = process.platform === 'win32' ? '.exe' : '';
const writableBinary = helperBundle();
const binaryDirectory = dirname(writableBinary);
const producerBinary = join(binaryDirectory, `cso-eval-producer${suffix}`);
if (process.platform !== 'win32') {
chmodSync(binaryDirectory, 0o555);
chmodSync(producerBinary, 0o555);
chmodSync(join(binaryDirectory, `gstack-cso-core${suffix}`), 0o555);
chmodSync(join(binaryDirectory, `gstack-cso-watchdog${suffix}`), 0o555);
chmodSync(writableBinary, 0o755);
}
const writableBinaryBinding = resolveProducerHelperBinding(source, state, writableBinary);
const binaryError = () => validateProductionProducerInstallation(writableBinaryBinding, producerBinary);
if (typeof process.getuid === 'function' && process.getuid() === 0) expect(binaryError).toThrow('ROOT_PRODUCER_UNSUPPORTED');
else expect(binaryError).toThrow('WRITABLE_PRODUCER_INSTALLATION');
if (process.platform !== 'win32') chmodSync(binaryDirectory, 0o755);
const writableDirectory = helperBundle();
const directory = dirname(writableDirectory);
const adjacentProducer = join(directory, `cso-eval-producer${suffix}`);
if (process.platform !== 'win32') {
for (const path of [writableDirectory, join(directory, `gstack-cso-core${suffix}`), join(directory, `gstack-cso-watchdog${suffix}`), adjacentProducer]) chmodSync(path, 0o555);
chmodSync(directory, 0o755);
}
const writableDirectoryBinding = resolveProducerHelperBinding(source, state, writableDirectory);
const directoryError = () => validateProductionProducerInstallation(writableDirectoryBinding, adjacentProducer);
if (typeof process.getuid === 'function' && process.getuid() === 0) expect(directoryError).toThrow('ROOT_PRODUCER_UNSUPPORTED');
else expect(directoryError).toThrow('WRITABLE_PRODUCER_INSTALLATION');
});
test('requires explicit authorization, consumes labels before the model starts, and records measured receipts', async () => {
const destination = join(root(), 'prepared');
const schedule = prepareEvalJobs(producerMatrix, skills, destination, selected.map(cell => cell.id));
const cell = selected.find(item => item.version === 'v2')!;
const inPlace = join(destination, 'jobs', cell.id, 'producer-input.json');
const receipts = join(root(), 'receipts'); mkdirSync(receipts);
const output = join(receipts, `${cell.id}.json`);
const invalidLauncher = join(root(), 'missing', `gstack-cso-launcher${process.platform === 'win32' ? '.exe' : ''}`);
await expect(runProducerCell(inPlace, output, { adapter: new FakeAdapter(() => {}), paidExecutionAuthorized: false, testHelperLauncherPath: invalidLauncher })).rejects.toThrow('PAID_EXECUTION_NOT_AUTHORIZED');
await expect(runProducerCell(inPlace, output, { adapter: new FakeAdapter(() => {}), paidExecutionAuthorized: true, testHelperLauncherPath: invalidLauncher })).rejects.toThrow('NON_ISOLATED_PRODUCER_LAYOUT');
const isolated = isolate(destination, cell), input = isolated.input;
const launcher = helperBundle();
const adapter = new FakeAdapter(opts => {
expect(existsSync(input)).toBe(false);
expect(opts).toMatchObject({
model: cell.model,
timeoutMs: cell.budgetSeconds * 1000,
workdir: join(isolated.job, 'state'),
csoProducer: {
stateDirectory: join(isolated.job, 'state'),
sourceDirectory: isolated.source,
helperLauncher: launcher,
helperGeneration: join(dirname(launcher), '.gstack-cso-generation'),
providerCommand: providerCommandFor(launcher),
},
});
expect(opts.extraArgs).toBeUndefined();
expect(readdirSync(isolated.job).sort()).toEqual(['source', 'state']);
expect(opts.prompt).toContain(skills.v2);
expect(opts.prompt).toContain('/cso --budget 600');
expect(opts.prompt).toContain(`application repository at ${isolated.source}`);
expect(opts.prompt).toContain('permission profile grants read-only access to exactly that immutable snapshot');
expect(opts.prompt).toContain('Use only the trusted helper to inspect or act on source');
expect(opts.prompt).toContain(`absolute launcher path ${JSON.stringify(launcher)}`);
expect(opts.prompt).toContain('do not discover or invoke another helper through PATH');
expect(opts.prompt).not.toContain(cell.caseId);
expect(opts.prompt).not.toContain(cell.variant);
expect(opts.prompt).not.toContain('ORACLE_SQL_MARKER');
const artifactRoot = join(process.env.GSTACK_HOME!, 'security', 'cso', 'run-1');
mkdirSync(artifactRoot, { recursive: true });
writeFileSync(join(artifactRoot, 'report.json'), '{"status":"complete"}\n');
writeFileSync(join(artifactRoot, 'repair.patch'), 'diff --git a/app.mjs b/app.mjs\n');
if (process.platform !== 'win32') {
expect(statSync(isolated.source).mode & 0o777).toBe(0o555);
expect(statSync(join(isolated.source, 'app.mjs')).mode & 0o777).toBe(0o444);
}
expect(process.env.GSTACK_SESSION_KIND).toBe('spawned');
expect(process.env.GSTACK_HEADLESS).toBe('1');
});
await expect(runProducerCell(input, output, { adapter, paidExecutionAuthorized: false, testHelperLauncherPath: invalidLauncher })).rejects.toThrow('PAID_EXECUTION_NOT_AUTHORIZED');
expect(existsSync(input)).toBe(true);
const receipt = await runProducerCell(input, output, withHelper({ adapter, paidExecutionAuthorized: true }, launcher));
expect(receipt).toMatchObject({ status: 'succeeded', requestedModel: 'exact-eval-model', modelUsed: 'gpt-5.4', modelIdentitySource: 'provider_reported', durationMs: 1234, firstUsefulResultMs: null, usage: { inputTokens: 120, outputTokens: 30, cachedTokens: 10, estimatedCostUSD: 0.0042 } });
expect(receipt.installationIdentity.identityHash).toMatch(/^[a-f0-9]{64}$/);
expect(receipt.installationIdentity.schemaVersion).toBe(1);
expect(receipt.installationIdentity.generation.coreSha256).toBe(receipt.installationIdentity.core.sha256);
expect(receipt.installationIdentity.generation.manifest).toEqual({
sha256: sha256(`${receipt.installationIdentity.core.sha256}\n`),
bytes: 65,
});
for (const artifact of [receipt.installationIdentity.producer, receipt.installationIdentity.launcher, receipt.installationIdentity.core, receipt.installationIdentity.watchdog, receipt.installationIdentity.generation.manifest]) {
expect(artifact.bytes).toBeGreaterThan(0);
expect(artifact.sha256).toMatch(/^[a-f0-9]{64}$/);
}
expect(receipt.providerIdentity).toEqual(providerIdentityFor('gpt', launcher));
expect(receipt.artifacts).toMatchObject({ schemaVersion: 1, root: 'security/cso' });
expect(receipt.artifacts.totalBytes).toBe(receipt.artifacts.entries.reduce((sum, entry) => sum + entry.bytes, 0));
expect(receipt.artifacts.entries.map(entry => entry.path)).toEqual(['run-1/repair.patch', 'run-1/report.json']);
expect(receipt.artifacts.identityHash).toBe(producerArtifactInventoryHash({ schemaVersion: 1, root: 'security/cso', entries: receipt.artifacts.entries, totalBytes: receipt.artifacts.totalBytes }));
const retainedHome = join(isolated.job, 'state', 'cso-home');
expect(readFileSync(join(retainedHome, 'security', 'cso', 'run-1', 'report.json'), 'utf8')).toContain('complete');
expect(receipt.artifacts.entries.every(entry => !/provider|credential|session/i.test(entry.path))).toBe(true);
expect(JSON.parse(readFileSync(output, 'utf8'))).toEqual(receipt);
const { receiptHash, ...receiptWithoutHash } = receipt;
expect(producerReceiptHash(receiptWithoutHash)).toBe(receiptHash);
const batch = collectProducerReceipts(producerMatrix, schedule, [receipt]);
expect(batch.summary).toMatchObject({ scheduled: 960, prepared: 2, submitted: 1, missing: 959, matchedPairsExpected: 480, matchedPairsSubmitted: 0, modelMismatches: [] });
expect(batch.summary.groups.find(group => group.version === 'v2' && group.mode === 'daily')).toMatchObject({ scheduled: 240, submitted: 1, missing: 239, latency: { samples: 1, denominator: 240, p95Ms: 1234 }, firstUsefulResult: { samples: 0, denominator: 240, p95Ms: null }, modelTokens: { measured: 1, denominator: 240, total: 150 }, estimatedCost: { measured: 1, denominator: 240, totalUSD: 0.0042 } });
});
test('passes the same producer policy to every host and removes Gemini CLI state', async () => {
for (const [host, family] of [['codex', 'gpt'], ['claude', 'claude'], ['gemini', 'gemini']] as const) {
const hostMatrix = createEvalMatrix({ model: 'exact-eval-model', host, skillHashes: { v2: sha256(skills.v2), v3: sha256(skills.v3) } });
const cell = hostMatrix.cells.find(item => item.caseId === 'node-sql-injection' && item.variant === 'vulnerable' && item.version === 'v3' && item.mode === 'daily' && item.repetition === 1)!;
const destination = join(root(), `${host}-prepared`);
prepareEvalJobs(hostMatrix, skills, destination, [cell.id]);
const isolated = isolate(destination, cell), state = join(isolated.job, 'state'), launcher = helperBundle();
const outputRoot = join(root(), `${host}-receipts`); mkdirSync(outputRoot);
const adapter = new FakeAdapter(opts => {
expect(opts.csoProducer).toEqual({
stateDirectory: state,
sourceDirectory: isolated.source,
helperLauncher: launcher,
helperGeneration: join(dirname(launcher), '.gstack-cso-generation'),
providerCommand: providerCommandFor(launcher),
});
expect(opts.extraArgs).toBeUndefined();
if (host === 'gemini') {
const paths = geminiProducerPaths(state);
expect(JSON.parse(readFileSync(paths.systemDefaults, 'utf8'))).toEqual({});
const settings = JSON.parse(readFileSync(paths.systemSettings, 'utf8'));
const contextFileName = settings.context.fileName[0];
expect(contextFileName).toMatch(/^\.gstack-cso-context-[a-f0-9]{32}\.md$/);
expect(settings).toEqual(geminiProducerSystemSettings(contextFileName, launcher));
expect(statSync(paths.root).mode & 0o777).toBe(0o700);
expect(statSync(paths.home).mode & 0o777).toBe(0o700);
expect(statSync(paths.workdir).mode & 0o777).toBe(0o700);
expect(statSync(paths.systemSettings).mode & 0o777).toBe(0o600);
expect(readdirSync(paths.workdir)).toEqual([]);
for (const directory of [paths.root, paths.home, paths.workdir]) {
expect(existsSync(join(directory, 'GEMINI.md'))).toBe(false);
expect(existsSync(join(directory, 'AGENTS.md'))).toBe(false);
expect(existsSync(join(directory, 'MEMORY.md'))).toBe(false);
}
expect(opts.prompt).toContain(`application repository at ${isolated.source}`);
expect(opts.prompt).toContain('neutral current working directory');
if (process.platform !== 'win32') {
expect(statSync(isolated.source).mode & 0o777).toBe(0o555);
expect(statSync(join(isolated.source, 'app.mjs')).mode & 0o777).toBe(0o444);
}
}
}, cell.model, family);
const receipt = await runProducerCell(isolated.input, join(outputRoot, `${cell.id}.json`), withHelper({ adapter, paidExecutionAuthorized: true }, launcher));
expect(receipt.status).toBe('succeeded');
if (host === 'gemini') expect(existsSync(geminiProducerPaths(state).root)).toBe(false);
}
});
test('removes disposable Gemini CLI state when the provider throws', async () => {
const hostMatrix = createEvalMatrix({ model: 'exact-eval-model', host: 'gemini', skillHashes: { v2: sha256(skills.v2), v3: sha256(skills.v3) } });
const cell = hostMatrix.cells.find(item => item.caseId === 'node-sql-injection' && item.variant === 'vulnerable' && item.version === 'v3' && item.mode === 'daily' && item.repetition === 1)!;
const destination = join(root(), 'gemini-throw-prepared');
prepareEvalJobs(hostMatrix, skills, destination, [cell.id]);
const isolated = isolate(destination, cell), state = join(isolated.job, 'state');
const outputRoot = join(root(), 'gemini-throw-receipts'); mkdirSync(outputRoot);
const adapter: ProviderAdapter = {
name: 'fake', family: 'gemini',
async available() { return { ok: true }; },
async run() { expect(existsSync(geminiProducerPaths(state).root)).toBe(true); throw new Error('provider stopped'); },
estimateCost() { return 0; },
};
await expect(runProducerCell(isolated.input, join(outputRoot, `${cell.id}.json`), withHelper({ adapter, paidExecutionAuthorized: true }))).rejects.toThrow('provider stopped');
expect(existsSync(geminiProducerPaths(state).root)).toBe(false);
});
test('records an exit-zero blank provider result as a failed receipt',async()=>{
const destination=join(root(),'blank-prepared'),cell=selected[0];
prepareEvalJobs(producerMatrix,skills,destination,[cell.id]);
const isolated=isolate(destination,cell),output=join(root(),`${cell.id}.json`);
const receipt=await runProducerCell(isolated.input,output,withHelper({adapter:new FakeAdapter(()=>{},cell.model,'gpt',{output:' \n',tokens:{input:0,output:0}}),paidExecutionAuthorized:true}));
expect(receipt.status).toBe('failed');
expect(receipt.error).toEqual({code:'unknown',reason:'empty output from provider CLI (exit 0)'});
expect(receipt.outputHash).toBe(sha256(receipt.output));
expect(JSON.parse(readFileSync(output,'utf8'))).toEqual(receipt);
});
test('preserves provider-declared exit-zero errors as failed receipts',async()=>{
const destination=join(root(),'provider-error-prepared'),cell=selected[0];prepareEvalJobs(producerMatrix,skills,destination,[cell.id]);
const isolated=isolate(destination,cell),output=join(root(),`${cell.id}.json`),error={code:'unknown' as const,reason:'empty or invalid output from claude CLI (exit 0)'};
const receipt=await runProducerCell(isolated.input,output,withHelper({adapter:new FakeAdapter(()=>{},cell.model,'gpt',{output:'',error}),paidExecutionAuthorized:true}));
expect(receipt.status).toBe('failed');expect(receipt.error).toEqual(error);expect(receipt.outputHash).toBe(sha256(receipt.output));
});
test('redacts producer output and split error canaries into unsuccessful receipts', async () => {
for (const [suffix, outputText, error] of [
['output', `report contains sk-proj-${'a'.repeat(40)}`, undefined],
['split', 'diagnostic ends with sk-proj-', { code: 'unknown' as const, reason: 'b'.repeat(40) }],
] as const) {
const destination = join(root(), `${suffix}-prepared`);
const cell = selected[0];
prepareEvalJobs(producerMatrix, skills, destination, [cell.id]);
const isolated = isolate(destination, cell);
const outputRoot = join(root(), `${suffix}-receipts`); mkdirSync(outputRoot);
const receiptPath = join(outputRoot, `${cell.id}.json`);
const adapter = new FakeAdapter(() => {}, cell.model, 'gpt', { output: outputText, ...(error ? { error } : {}) });
const receipt = await runProducerCell(isolated.input, receiptPath, withHelper({ adapter, paidExecutionAuthorized: true }));
expect(receipt.status).toBe('failed');
expect(receipt.output).toBe('[sensitive producer output redacted]');
expect(receipt.outputHash).toBe(sha256(receipt.output));
expect(receipt.error).toEqual({ code: 'unknown', reason: 'Sensitive producer output or error withheld' });
const stored = readFileSync(receiptPath, 'utf8');
expect(stored).not.toContain('sk-proj-');
expect(stored).not.toContain('b'.repeat(40));
}
});
test('withholds the receipt when the bound provider executable changes during a cell', async () => {
const destination = join(root(), 'provider-race-prepared'), cell = selected[0];
prepareEvalJobs(producerMatrix, skills, destination, [cell.id]);
const isolated = isolate(destination, cell), launcher = helperBundle();
const outputRoot = join(root(), 'provider-race-receipts'); mkdirSync(outputRoot);
const receiptPath = join(outputRoot, `${cell.id}.json`);
const adapter = new FakeAdapter(opts => writeFileSync(opts.csoProducer!.providerCommand.executable, '#!/bin/sh\nexit 1\n'));
await expect(runProducerCell(isolated.input, receiptPath, withHelper({ adapter, paidExecutionAuthorized: true }, launcher))).rejects.toThrow('PRODUCER_PROVIDER_INSTALLATION_RACE');
expect(existsSync(receiptPath)).toBe(false);
});
test('withholds the receipt when the helper generation changes during a cell', async () => {
const destination = join(root(), 'helper-generation-race-prepared'), cell = selected[0];
prepareEvalJobs(producerMatrix, skills, destination, [cell.id]);
const isolated = isolate(destination, cell), launcher = helperBundle();
const outputRoot = join(root(), 'helper-generation-race-receipts'); mkdirSync(outputRoot);
const receiptPath = join(outputRoot, `${cell.id}.json`);
const adapter = new FakeAdapter(opts => {
const core = join(dirname(opts.csoProducer!.helperLauncher), `gstack-cso-core${process.platform === 'win32' ? '.exe' : ''}`);
writeFileSync(core, process.platform === 'win32' ? 'new test executable\n' : '#!/bin/sh\nexit 1\n', { mode: 0o755 });
writeFileSync(opts.csoProducer!.helperGeneration, `${sha256(readFileSync(core))}\n`);
});
await expect(runProducerCell(isolated.input, receiptPath, withHelper({ adapter, paidExecutionAuthorized: true }, launcher))).rejects.toThrow('PRODUCER_HELPER_GENERATION_CHANGED');
expect(existsSync(receiptPath)).toBe(false);
});
test('fails closed without a receipt or raw console reason when redaction cannot inspect output', async () => {
const destination = join(root(), 'redaction-failure-prepared'), cell = selected[0];
prepareEvalJobs(producerMatrix, skills, destination, [cell.id]);
const isolated = isolate(destination, cell);
const outputRoot = join(root(), 'redaction-failure-receipts'); mkdirSync(outputRoot);
const receiptPath = join(outputRoot, `${cell.id}.json`);
const canary = 'secret-canary-' + 'x'.repeat(1024 * 1024 + 1);
let failure: unknown;
try {
await runProducerCell(isolated.input, receiptPath, withHelper({ adapter: new FakeAdapter(() => {}, cell.model, 'gpt', { output: canary }), paidExecutionAuthorized: true }));
} catch (error) { failure = error; }
expect(failure).toBeInstanceOf(CsoError);
expect((failure as CsoError).code).toBe('REDACTION_FAILED');
expect(existsSync(receiptPath)).toBe(false);
expect(producerFailureMessage(failure)).toBe('REDACTION_FAILED: producer payload withheld');
expect(producerFailureMessage(failure)).not.toContain(canary.slice(0, 32));
});
test('rejects effective-model mismatches between otherwise matched v2 and v3 cells', async () => {
const destination = join(root(), 'prepared');
const schedule = prepareEvalJobs(producerMatrix, skills, destination, selected.map(cell => cell.id));
const receiptsRoot = join(root(), 'receipts'); mkdirSync(receiptsRoot);
const receipts = [];
for (const cell of selected) {
const adapter = new FakeAdapter(() => {}, cell.version === 'v2' ? 'resolved-a' : 'resolved-b');
const isolated = isolate(destination, cell);
receipts.push(await runProducerCell(isolated.input, join(receiptsRoot, `${cell.id}.json`), withHelper({ adapter, paidExecutionAuthorized: true })));
}
expect(() => collectProducerReceipts(producerMatrix, schedule, receipts)).toThrow('UNMATCHED_EFFECTIVE_MODELS');
const tampered = structuredClone(receipts[0]); tampered.output += 'changed';
expect(() => collectProducerReceipts(producerMatrix, schedule, [tampered])).toThrow('INVALID_PRODUCER_RECEIPT');
});
test('seals source read-only and withholds a receipt after a mode/content mutation', async () => {
const destination = join(root(), 'prepared');
const cell = selected[0];
prepareEvalJobs(producerMatrix, skills, destination, [cell.id]);
const isolated = isolate(destination, cell);
const receipt = join(root(), `${cell.id}.json`);
const original = readFileSync(join(isolated.source, 'app.mjs'), 'utf8');
const adapter = new FakeAdapter(opts => {
const source = opts.csoProducer!.sourceDirectory;
expect(source).toBe(isolated.source);
if (process.platform !== 'win32') {
expect(statSync(source).mode & 0o777).toBe(0o555);
expect(statSync(join(source, 'app.mjs')).mode & 0o777).toBe(0o444);
// Simulate a stronger same-UID compromise: postchecks must still detect it.
chmodSync(join(source, 'app.mjs'), 0o644);
}
writeFileSync(join(source, 'app.mjs'), 'changed by producer\n');
});
await expect(runProducerCell(isolated.input, receipt, withHelper({ adapter, paidExecutionAuthorized: true }))).rejects.toThrow(process.platform === 'win32' ? 'INVALID_PRODUCER_SOURCE' : 'PRODUCER_CHANGED_SOURCE_MODE');
expect(readFileSync(join(isolated.source, 'app.mjs'), 'utf8')).not.toBe(original);
expect(existsSync(receipt)).toBe(false);
});
});
+134
View File
@@ -0,0 +1,134 @@
import { afterEach, describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { capture } from '../lib/cso/snapshot';
import { childEnvironment, executable, git as readGitMetadata, runProcess } from '../lib/cso/process';
const roots:string[]=[];
afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
function fixture(){
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-git-hardening-')),repo=path.join(root,'repo'),runDir=path.join(root,'state','run');
roots.push(root);fs.mkdirSync(repo);fs.mkdirSync(runDir,{recursive:true,mode:0o700});
const git=(...args:string[])=>{const result=spawnSync('/usr/bin/git',['-C',repo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);return result.stdout;};
git('init','-q');git('config','user.email','fixture@example.test');git('config','user.name','Fixture');
fs.writeFileSync(path.join(repo,'tracked.ts'),'export const tracked = true\n');git('add','tracked.ts');git('commit','-qm','base');
return{root,repo,runDir,git};
}
function replaceWithSymlinkAfterLstat(target:string,replacement:string,occurrence=1){
const original=fs.lstatSync;let hits=0,swapped=false;
const patched=spyOn(fs,'lstatSync').mockImplementation(((candidate:any,options?:any)=>{
const result=options===undefined?original(candidate):original(candidate,options);
if(path.resolve(String(candidate))===target&&++hits===occurrence){
fs.renameSync(target,`${target}.parked`);fs.symlinkSync(replacement,target);swapped=true;
}
return result;
}) as typeof fs.lstatSync);
return{patched,wasSwapped:()=>swapped};
}
describe('CSO Git metadata hardening',()=>{
test.skipIf(process.platform==='win32')('reports a bounded operation and exit status without exposing Git argv or paths',async()=>{
const {root,repo}=fixture();let failure:any;
try{await readGitMetadata(repo,['rev-parse','--verify','secret-ref-name'],root);}catch(error){failure=error;}
expect(failure).toMatchObject({code:'MISSING_INPUT'});
expect(failure.message).toContain('rev-parse exited');
expect(failure.message).toContain('(request rejected)');
expect(failure.message).not.toContain(root);
expect(failure.message).not.toContain('secret-ref-name');
});
test.skipIf(process.platform==='win32')('labels the fixed object-format phase without exposing repository data',async()=>{
const {root,repo}=fixture();fs.appendFileSync(path.join(repo,'.git','config'),'\n[broken configuration\n');let failure:any;
try{await readGitMetadata(repo,['rev-parse','--show-object-format'],root);}catch(error){failure=error;}
expect(failure).toMatchObject({code:'MISSING_INPUT'});
expect(failure.message).toContain('object-format exited');
expect(failure.message).toContain('(configuration rejected)');
expect(failure.message).not.toContain(root);
});
test.skipIf(process.platform==='win32')('refuses trusted Git calls that are not bound to one audited worktree',async()=>{
const {root}=fixture();
await expect(runProcess(executable('git'),['rev-parse','--is-inside-work-tree'],{cwd:root,env:childEnvironment(root),raw:true})).rejects.toMatchObject({code:'INVALID_ARGUMENT'});
});
test.skipIf(process.platform==='win32')('pins every metadata read to the audited worktree and ignores configured global excludes',async()=>{
const {root,repo,runDir,git}=fixture(),empty=path.join(root,'decoy-worktree'),excludes=path.join(root,'global-excludes');
fs.mkdirSync(empty);fs.writeFileSync(excludes,'untracked-security.ts\n');
fs.writeFileSync(path.join(repo,'untracked-security.ts'),'export const vulnerable = true\n');
fs.writeFileSync(path.join(repo,'TRACKED.ts'),'export const caseVariant = true\n');
git('config','core.worktree',empty);git('config','core.excludesFile',excludes);git('config','core.ignoreCase','true');git('config','core.precomposeUnicode','true');
const manifest=await capture(repo,runDir,'HEAD');
expect(manifest.entries.map(entry=>entry.path)).toContain('untracked-security.ts');
expect(manifest.entries.map(entry=>entry.path)).toContain('TRACKED.ts');
expect(fs.readFileSync(path.join(runDir,'snapshot','untracked-security.ts'),'utf8')).toContain('vulnerable');
expect(manifest.changedPaths).toContain('untracked-security.ts');
});
test.skipIf(process.platform==='win32')('ignores replacement refs when retaining tracked deletions and history',async()=>{
const {repo,runDir,git}=fixture(),head=git('rev-parse','HEAD').trim(),emptyTree=git('mktree').trim(),replacement=git('commit-tree',emptyTree,'-m','replacement-history').trim();
git('replace',head,replacement);git('rm','-q','-f','tracked.ts');
const manifest=await capture(repo,runDir);
const history=fs.readFileSync(path.join(runDir,'history.txt'),'utf8');
expect(manifest.headCommit).toBe(head);
expect(manifest.deletedPaths).toEqual([{path:'tracked.ts',pathId:expect.stringMatching(/^[a-f0-9]{32}$/)}]);
expect(history).toContain('Subject: base');
expect(history).not.toContain('replacement-history');
});
test.skipIf(process.platform==='win32')('rejects repository config includes before Git can consume them',async()=>{
const {root,repo,runDir}=fixture(),included=path.join(root,'included.conf');
fs.writeFileSync(included,'[core]\n\tworktree = /tmp/cso-decoy\n');
fs.appendFileSync(path.join(repo,'.git','config'),`\n[include]\n\tpath = ${included}\n`);
await expect(capture(repo,runDir)).rejects.toMatchObject({code:'UNSAFE_PATH',message:'Repository Git config includes are not allowed during a security snapshot'});
});
test.skipIf(process.platform==='win32')('binds an absent main-worktree config so it cannot appear after inspection',async()=>{
const {root,repo,git}=fixture(),worktreeConfig=path.join(repo,'.git','config.worktree'),lstat=fs.lstatSync;
git('config','extensions.worktreeConfig','true');let injected=false;
const patched=spyOn(fs,'lstatSync').mockImplementation(((candidate:any,options?:any)=>{
try{return options===undefined?lstat(candidate):lstat(candidate,options);}catch(error:any){
if(!injected&&path.resolve(String(candidate))===worktreeConfig&&error?.code==='ENOENT'){
fs.writeFileSync(worktreeConfig,'[cso]\n\tmarker = created-after-inspection\n');injected=true;
}
throw error;
}
}) as typeof fs.lstatSync);
try{await expect(runProcess(executable('git'),['--no-optional-locks','-C',repo,'rev-parse','--is-inside-work-tree'],{cwd:root,env:childEnvironment(root),raw:true})).rejects.toMatchObject({code:'SNAPSHOT_RACE'});}finally{patched.mockRestore();}
expect(injected).toBe(true);
});
test.skipIf(process.platform==='win32')('does not follow a repository config swapped after its bounded lstat',async()=>{
const {root,repo,runDir}=fixture(),config=path.join(repo,'.git','config'),oversized=path.join(root,'oversized-config');
fs.writeFileSync(oversized,'[core]\n'+'.'.repeat(1024*1024));
const race=replaceWithSymlinkAfterLstat(config,oversized);
try{await expect(capture(repo,runDir)).rejects.toMatchObject({code:'SNAPSHOT_RACE'});}finally{race.patched.mockRestore();}
expect(race.wasSwapped()).toBe(true);
});
test.skipIf(process.platform==='win32')('does not follow a worktree .git pointer swapped between lstat and open',async()=>{
const {root,repo,runDir}=fixture(),gitDir=path.join(root,'git-data'),marker=path.join(repo,'.git'),oversized=path.join(root,'oversized-git-pointer');
fs.renameSync(marker,gitDir);fs.writeFileSync(marker,'gitdir: ../git-data\n');fs.writeFileSync(oversized,'gitdir: '+'.'.repeat(16*1024));
const race=replaceWithSymlinkAfterLstat(marker,oversized,2);
try{await expect(capture(repo,runDir)).rejects.toMatchObject({code:'SNAPSHOT_RACE'});}finally{race.patched.mockRestore();}
expect(race.wasSwapped()).toBe(true);
});
test.skipIf(process.platform==='win32')('does not follow a linked-worktree commondir pointer swapped after lstat',async()=>{
const {root,repo,git}=fixture(),linked=path.join(root,'linked');
git('worktree','add','-q','-b','linked-security-test',linked);
const marker=fs.readFileSync(path.join(linked,'.git'),'utf8').trim().replace(/^gitdir:\s*/,''),gitDir=fs.realpathSync(path.resolve(linked,marker)),common=path.join(gitDir,'commondir'),oversized=path.join(root,'oversized-commondir');
fs.writeFileSync(oversized,'.'.repeat(16*1024));
const race=replaceWithSymlinkAfterLstat(common,oversized);
try{await expect(runProcess(executable('git'),['--no-optional-locks','-C',linked,'rev-parse','--is-inside-work-tree'],{cwd:root,env:childEnvironment(root),raw:true})).rejects.toMatchObject({code:'SNAPSHOT_RACE'});}finally{race.patched.mockRestore();}
expect(race.wasSwapped()).toBe(true);
});
});
+32
View File
@@ -0,0 +1,32 @@
import { describe,expect,test } from 'bun:test';
import { gitDiffHeaderPaths,historyForPath } from '../lib/cso/history';
describe('CSO retained Git history path filtering',()=>{
test('matches exact paths without leaking a prefix sibling hunk',()=>{
const raw=`commit ${'a'.repeat(40)}
Author: Fixture
diff --git a/foo b/foo
--- a/foo
+++ b/foo
+wanted
diff --git a/foo-extra b/foo-extra
--- a/foo-extra
+++ b/foo-extra
+must-not-leak
`;
const selected=historyForPath(raw,'foo')!;
expect(selected).toContain('+wanted');
expect(selected).not.toContain('must-not-leak');
});
test('decodes Git C-quoted UTF-8 and space-bearing header paths',()=>{
expect(gitDiffHeaderPaths('diff --git "a/caf\\303\\251 file.ts" "b/caf\\303\\251 file.ts"')).toEqual(['a/café file.ts','b/café file.ts']);
const raw=`commit ${'b'.repeat(40)}\nSubject: Unicode\ndiff --git "a/caf\\303\\251 file.ts" "b/caf\\303\\251 file.ts"\n+unicode-only\n`;
expect(historyForPath(raw,'café file.ts')).toContain('+unicode-only');
});
test('rejects malformed quoted headers instead of broadening the match',()=>{
expect(gitDiffHeaderPaths('diff --git "a/foo b/foo')).toBeUndefined();
expect(historyForPath('diff --git "a/foo b/foo\n+secret\n','foo')).toBeUndefined();
});
});
+175
View File
@@ -0,0 +1,175 @@
import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import { join } from 'node:path';
import { spawnSync } from 'node:child_process';
import { dispatchCsoCommand, type CsoCliDependencies } from '../lib/cso/cli';
import { canonical, CsoError, sha256 } from '../lib/cso/contracts';
import { ISOLATION_POLICY_HASH } from '../lib/cso/docker';
import {
catalogImageProvisioningPolicy,
inspectCatalogImages,
openLocalCatalogImageSession,
provisionCatalogImages,
qualifiedCatalogImages,
type CatalogImageSession,
type QualifiedCatalogImage,
} from '../lib/cso/image-provisioning';
import { scannerVersionHash, type QualifiedScanner, type ScannerCatalog } from '../lib/cso/scanner-catalog';
import { SCANNER_IDS, scannerPlans, type ScannerId } from '../lib/cso/scanners';
import type { RuntimePlatform } from '../lib/cso/runtime-catalog';
import { completeRuntimeCatalogFixture } from './helpers/cso-runtime-catalog';
const roots:string[]=[];
afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
const target:RuntimePlatform=process.arch==='arm64'?'linux/arm64':'linux/amd64';
const hash='a'.repeat(64),digest=`sha256:${hash}`,sourceCommit='b'.repeat(40),workflow='https://github.com/garrytan/gstack/actions/runs/42';
function scannerProfile(scanner:ScannerId,platform:RuntimePlatform):QualifiedScanner{
const arch=platform.endsWith('arm64')?'arm64':'amd64',version=scanner==='osv'?'2.4.0':'4.0.0';
return{id:`${scanner}-provision-${arch}`,scanner,state:'qualified',platform,image:`ghcr.io/garrytan/gstack/cso-scanners/${scanner}-${arch}@${digest}`,
entrypoint:'/opt/cso/entrypoint',executable:'/opt/cso/bin/scanner',version,versionOutputSha256:scannerVersionHash(`${scanner} version ${version}\n`),helperAbi:3,isolationPolicyHash:ISOLATION_POLICY_HASH,
capabilities:scannerPlans({snapshotRoot:'/source',offline:true,selected:[scanner]})[0].requiredFeatures,
...(scanner==='semgrep'?{assets:{semgrepRules:{path:'/policy/catalog/semgrep.yml',sha256:hash}}}:{}),
...(['osv','trivy'].includes(scanner)?{assets:{advisoryDatabase:{path:'/opt/cso/scanner-data/db',contentSha256:hash,updatedAt:'2026-09-09T00:00:00.000Z',ecosystems:['npm']}}}:{}),
qualifiedAt:'2026-09-09T00:00:00.000Z',qualification:{sourceCommit,workflow,sbomDigest:digest,provenanceDigest:digest,verifiedProvenance:true,containmentPassed:true,adapterContractPassed:true,offlineAssetsPassed:true}};
}
function scannerCatalog():ScannerCatalog{
const scanners=SCANNER_IDS.flatMap(scanner=>[scannerProfile(scanner,'linux/amd64'),scannerProfile(scanner,'linux/arm64')]);
return{schemaVersion:1,revision:'scanner-provisioning-fixture',helperAbi:3,promotion:{sourceCommit,workflow,evidenceDigest:`sha256:${sha256(canonical(scanners))}`},scanners};
}
function session(options:{present?:(entry:QualifiedCatalogImage)=>boolean;pull?:(entry:QualifiedCatalogImage)=>void}={}):{value:CatalogImageSession;calls:{present:string[];pull:string[]};closed:()=>boolean}{
const calls={present:[] as string[],pull:[] as string[]};let closed=false;
return{calls,closed:()=>closed,value:{docker:{endpoint:'unix:///trusted/docker.sock',version:'27.0.0',security:['seccomp']},
present:async entry=>{calls.present.push(entry.image);return options.present?.(entry)??false;},
pull:async entry=>{calls.pull.push(entry.image);options.pull?.(entry);},close:()=>{closed=true;}}};
}
function nodeRepo():string{
const root=fs.mkdtempSync(join(os.tmpdir(),'cso-image-doctor-'));roots.push(root);
fs.writeFileSync(join(root,'package.json'),JSON.stringify({name:'doctor-fixture',version:'1.0.0'}));
fs.writeFileSync(join(root,'package-lock.json'),JSON.stringify({name:'doctor-fixture',version:'1.0.0',lockfileVersion:3,packages:{'':{name:'doctor-fixture',version:'1.0.0'}}}));
fs.writeFileSync(join(root,'app.js'),'export const ready = true\n');
for(const args of [['init','-q'],['config','user.email','fixture@example.test'],['config','user.name','Fixture'],['add','.'],['commit','-qm','fixture']]){
const result=spawnSync('/usr/bin/git',['-C',root,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);
}
return root;
}
function railsPostgresRepo():string{
const root=fs.mkdtempSync(join(os.tmpdir(),'cso-image-doctor-rails-'));roots.push(root);fs.mkdirSync(join(root,'config'));
fs.writeFileSync(join(root,'Gemfile'),'');
fs.writeFileSync(join(root,'Gemfile.lock'),`GEM\n remote: https://rubygems.org/\n specs:\n pg (1.5.9)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n pg\n\nRUBY VERSION\n ruby 3.3.6p108\n\nBUNDLED WITH\n 2.6.9\n`);
fs.writeFileSync(join(root,'config/database.yml'),'test:\n adapter: postgresql\n database: app_test\n');
for(const args of [['init','-q'],['config','user.email','fixture@example.test'],['config','user.name','Fixture'],['add','.'],['commit','-qm','fixture']]){
const result=spawnSync('/usr/bin/git',['-C',root,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);
}
return root;
}
describe('trusted CSO image provisioning',()=>{
test('setup derives a bounded allowance per declared catalog image',()=>{
expect(catalogImageProvisioningPolicy(11)).toEqual({perImageMs:30_000,aggregateMs:360_000});
expect(catalogImageProvisioningPolicy(11,'300')).toEqual({perImageMs:300_000,aggregateMs:3_330_000});
expect(()=>catalogImageProvisioningPolicy(11,'4')).toThrow('5..300');
expect(()=>catalogImageProvisioningPolicy(11,'301')).toThrow('5..300');
expect(()=>catalogImageProvisioningPolicy(12,'300')).toThrow('bounded setup preload capacity');
});
test('plans only qualified exact-digest images for the native platform',()=>{
const runtimes=completeRuntimeCatalogFixture('image-provisioning-fixture'),entries=qualifiedCatalogImages(runtimes,scannerCatalog(),target);
expect(entries).toHaveLength(11);
expect(entries.filter(entry=>entry.kind==='runtime')).toHaveLength(5);expect(entries.filter(entry=>entry.kind==='scanner')).toHaveLength(6);
expect(entries.every(entry=>entry.platform===target)).toBe(true);
expect(entries.every(entry=>/^ghcr\.io\/.+@sha256:[a-f0-9]{64}$/.test(entry.image))).toBe(true);
expect(entries.map(entry=>`${entry.kind}:${entry.id}`)).toEqual([...entries.map(entry=>`${entry.kind}:${entry.id}`)].sort());
});
test('doctor inspection is read-only and marks exact absent digests unavailable',async()=>{
const entries=qualifiedCatalogImages(completeRuntimeCatalogFixture('doctor-inspection-fixture'),scannerCatalog(),target),fake=session({present:()=>false});
const result=await inspectCatalogImages(entries,async()=>fake.value);
expect(result.docker.status).toBe('ready');
expect(result.images.every(item=>item.status==='unavailable'&&item.reason?.includes('not present'))).toBe(true);
expect(fake.calls.present).toEqual(entries.map(entry=>entry.image));
expect(fake.calls.pull).toEqual([]);
expect(fake.closed()).toBe(true);
});
test('preload pulls only missing entries and reports failures without disabling static audits',async()=>{
const entries=qualifiedCatalogImages(completeRuntimeCatalogFixture('setup-provisioning-fixture'),scannerCatalog(),target),present=entries[0],laterPresent=entries.at(-1)!,failed=entries[2],fake=session({
present:entry=>entry.image===present.image||entry.image===laterPresent.image,
pull:entry=>{if(entry.image===failed.image)throw new CsoError('PREREQUISITE','Public registry is unavailable');},
});
const result=await provisionCatalogImages(entries,target,async()=>fake.value);
expect(result).toMatchObject({status:'partial',downloads:true,requested:11,inspected:11,alreadyPresent:2,downloaded:1,deadlineReached:false});
expect(result.unavailable).toHaveLength(8);expect(result.unavailable[0]).toMatchObject({id:failed.id,status:'unavailable'});
expect(result.unavailable.at(-1)?.reason).toContain('Network provisioning stopped');
expect(fake.calls.present).toEqual(entries.map(entry=>entry.image));expect(fake.calls.pull).not.toContain(present.image);expect(fake.calls.pull).not.toContain(laterPresent.image);expect(fake.calls.pull).toHaveLength(2);expect(fake.closed()).toBe(true);
expect(result.summary).toContain('Rerun setup');
});
test('the aggregate deadline still bounds the complete provisioning pass',async()=>{
const entries=qualifiedCatalogImages(completeRuntimeCatalogFixture('setup-deadline-fixture'),scannerCatalog(),target).slice(0,4),started=Date.now(),deadline=started+100;
let receivedDeadline=0,presentCalls=0,pullCalls=0,closed=false;
const result=await provisionCatalogImages(entries,target,async value=>{receivedDeadline=value;return{docker:{endpoint:'unix:///trusted/docker.sock',version:'27.0.0',security:['seccomp']},
present:async()=>{presentCalls++;return false;},pull:async()=>{pullCalls++;while(Date.now()<value)await Bun.sleep(2);throw new CsoError('DEADLINE','Qualified image pull reached the aggregate preload deadline');},close:()=>{closed=true;}};},deadline);
expect(receivedDeadline).toBe(deadline);expect(Date.now()-started).toBeLessThan(1000);
expect(result).toMatchObject({status:'partial',requested:4,inspected:1,alreadyPresent:0,downloaded:0,deadlineReached:true});
expect(result.unavailable).toHaveLength(4);expect(result.summary).toContain('bounded aggregate deadline');
expect(presentCalls).toBe(1);expect(pullCalls).toBe(1);expect(closed).toBe(true);
});
test('one per-image timeout does not consume the remaining images allowance',async()=>{
const entries=qualifiedCatalogImages(completeRuntimeCatalogFixture('setup-per-image-fixture'),scannerCatalog(),target).slice(0,2),pulls:string[]=[];
const result=await provisionCatalogImages(entries,target,async deadline=>({docker:{endpoint:'unix:///trusted/docker.sock',version:'27.0.0',security:['seccomp']},
present:async()=>false,
pull:async(entry,imageDeadline=deadline)=>{pulls.push(entry.id);if(entry===entries[0]){while(Date.now()<imageDeadline)await Bun.sleep(1);throw new CsoError('DEADLINE','Per-image pull deadline reached');}},
close:()=>{},
}),Date.now()+1000,25);
expect(pulls).toEqual(entries.map(entry=>entry.id));
expect(result).toMatchObject({status:'partial',requested:2,inspected:2,downloaded:1,deadlineReached:false});
expect(result.unavailable).toEqual([expect.objectContaining({id:entries[0].id,reason:expect.stringContaining('per-image')})]);
expect(result.summary).toContain('GSTACK_CSO_IMAGE_PULL_TIMEOUT_SECONDS');
});
test('a local image check completing after the aggregate deadline cannot be counted',async()=>{
const entry=qualifiedCatalogImages(completeRuntimeCatalogFixture('setup-late-success-fixture'),scannerCatalog(),target)[0],deadline=Date.now()+25;
let pulls=0,closed=false;
const result=await provisionCatalogImages([entry],target,async()=>({docker:{endpoint:'unix:///trusted/docker.sock',version:'27.0.0',security:['seccomp']},
present:async()=>{while(Date.now()<deadline)await Bun.sleep(1);return true;},pull:async()=>{pulls++;},close:()=>{closed=true;}}),deadline);
expect(result).toMatchObject({status:'partial',requested:1,inspected:0,alreadyPresent:0,downloaded:0,deadlineReached:true});
expect(result.unavailable).toEqual([expect.objectContaining({id:entry.id,status:'unavailable',reason:expect.stringContaining('deadline')})]);
expect(pulls).toBe(0);expect(closed).toBe(true);
});
test('the production session rejects remote Docker before inspecting or pulling images',async()=>{
await expect(openLocalCatalogImageSession({HOME:os.tmpdir(),DOCKER_HOST:'tcp://127.0.0.1:2375'})).rejects.toThrow('Remote TCP');
});
test.skipIf(process.platform==='win32')('doctor and setup command contracts share exact local availability without doctor downloads',async()=>{
const repo=nodeRepo(),runtimeCatalog=completeRuntimeCatalogFixture('doctor-cli-fixture'),scanners=scannerCatalog(),fake=session({present:()=>false}),dependencies:CsoCliDependencies={
runtimeCatalog,scannerCatalog:scanners,catalogImageSession:async()=>fake.value,watchdogPath:()=>'/trusted/watchdog',
};
const doctor=await dispatchCsoCommand('doctor',['--repo',repo],dependencies) as any;
expect(doctor.downloads).toBe(false);expect(doctor.elapsedMs).toBeLessThan(30_000);
expect(doctor.checks.find((item:any)=>item.capability==='local-docker-isolation')).toMatchObject({status:'ready'});
const runtime=doctor.checks.find((item:any)=>item.capability==='qualified-runtimes');
expect(runtime).toMatchObject({status:'missing',detail:{availability:'unavailable'}});expect(runtime.detail.prerequisite).toContain('not present');
for(const scanner of SCANNER_IDS)expect(doctor.checks.find((item:any)=>item.capability===`scanner:${scanner}`)).toMatchObject({status:'missing',detail:{availability:'unavailable'}});
expect(fake.calls.pull).toEqual([]);
const setupFake=session({present:()=>true}),summary=await dispatchCsoCommand('provision-images',['--setup-summary','--per-image-seconds','5'],{
...dependencies,catalogImageSession:async()=>setupFake.value,
});
expect(summary).toBe('Qualified CSO images ready: 11 available (0 downloaded, 11 already local).');
expect(setupFake.calls.pull).toEqual([]);
await expect(dispatchCsoCommand('provision-images',['--per-image-seconds','301'],dependencies)).rejects.toThrow('5..300');
});
test.skipIf(process.platform==='win32')('doctor includes the required PostgreSQL sidecar in Rails readiness without pulling',async()=>{
const repo=railsPostgresRepo(),runtimeCatalog=completeRuntimeCatalogFixture('doctor-rails-postgresql-fixture'),fake=session({present:entry=>!entry.id.includes('postgresql')}),dependencies:CsoCliDependencies={
runtimeCatalog,scannerCatalog:scannerCatalog(),catalogImageSession:async()=>fake.value,watchdogPath:()=>'/trusted/watchdog',
};
const doctor=await dispatchCsoCommand('doctor',['--repo',repo],dependencies) as any,runtime=doctor.checks.find((item:any)=>item.capability==='qualified-runtimes');
expect(doctor.downloads).toBe(false);expect(doctor.checks.find((item:any)=>item.capability==='application-preparation')).toMatchObject({status:'ready'});
expect(runtime).toMatchObject({status:'missing',detail:{availability:'unavailable',requiredSidecars:[{kind:'postgresql',availability:'unavailable'}]}});
expect(runtime.detail.requiredSidecars[0].prerequisite).toContain('not present');expect(fake.calls.pull).toEqual([]);expect(fake.closed()).toBe(true);
});
});
+53
View File
@@ -0,0 +1,53 @@
import { afterEach, describe, expect, test } from 'bun:test';
import { createHash } from 'node:crypto';
import { chmodSync, existsSync, linkSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
const ROOT=resolve(import.meta.dir,'..'),temps:string[]=[];
afterEach(()=>{for(const dir of temps.splice(0))rmSync(dir,{recursive:true,force:true});});
function temporary(){const dir=mkdtempSync(join(tmpdir(),'cso-generation-'));temps.push(dir);return dir;}
function digest(path:string){return createHash('sha256').update(readFileSync(path)).digest('hex');}
function compileC(source:string,output:string,args:string[]=[]){const result=spawnSync('/usr/bin/cc',['-std=c11','-D_POSIX_C_SOURCE=200809L','-O2',...args,source,'-o',output],{encoding:'utf8',timeout:30_000});expect(result.status,result.stderr).toBe(0);chmodSync(output,0o755);}
function compileLauncher(directory:string,coreDigest:string,testing=false){const output=join(directory,'gstack-cso-launcher'),args=[`-DGSTACK_CSO_CORE_SHA256=\"${coreDigest}\"`,...(testing?['-DGSTACK_CSO_TESTING']:[])];compileC(join(ROOT,'lib/cso/launcher.c'),output,args);return output;}
async function waitFor(path:string){const deadline=Date.now()+5000;while(!existsSync(path)&&Date.now()<deadline)await Bun.sleep(10);expect(existsSync(path)).toBe(true);}
describe.skipIf(process.platform==='win32')('CSO native generation handoff',()=>{
test('the launcher rejects changed core bytes even when the generation manifest is unchanged',()=>{
const directory=temporary(),core=join(directory,'gstack-cso-core'),source=join(directory,'core.c');
writeFileSync(source,'#include <stdio.h>\nint main(void){puts("OLD");return 0;}\n');compileC(source,core);const expected=digest(core),launcher=compileLauncher(directory,expected);writeFileSync(join(directory,'.gstack-cso-generation'),`${expected}\n`);
writeFileSync(source,'#include <stdio.h>\nint main(void){puts("CHANGED");return 0;}\n');compileC(source,core);
const result=spawnSync(launcher,[],{cwd:directory,encoding:'utf8',timeout:5000});expect(result.status).toBe(69);expect(result.stdout).not.toContain('CHANGED');expect(result.stderr).toContain('digest does not match');
});
test('the launcher rejects a multiply-linked core before execution',()=>{
const directory=temporary(),core=join(directory,'gstack-cso-core'),source=join(directory,'core.c');
writeFileSync(source,'#include <stdio.h>\nint main(void){puts("UNSAFE");return 0;}\n');compileC(source,core);const expected=digest(core),launcher=compileLauncher(directory,expected);writeFileSync(join(directory,'.gstack-cso-generation'),`${expected}\n`);linkSync(core,join(directory,'core-alias'));
const result=spawnSync(launcher,[],{cwd:directory,encoding:'utf8',timeout:5000});expect(result.status).toBe(69);expect(result.stdout).not.toContain('UNSAFE');expect(result.stderr).toContain('trusted compiled helper is missing');
});
test.skipIf(process.platform!=='linux')('the launcher executes the verified descriptor across a noncooperative pathname swap',async()=>{
const directory=temporary(),core=join(directory,'gstack-cso-core'),source=join(directory,'core.c'),ready=join(directory,'ready'),release=join(directory,'release');
writeFileSync(source,'#include <stdio.h>\nint main(void){puts("VERIFIED");return 0;}\n');compileC(source,core);const expected=digest(core),launcher=compileLauncher(directory,expected,true);writeFileSync(join(directory,'.gstack-cso-generation'),`${expected}\n`);
const running=Bun.spawn([launcher,'__cso-test-pause-after-core-verification',ready,release],{cwd:directory,stdout:'pipe',stderr:'pipe'});await waitFor(ready);
const replacement=join(directory,'replacement-core');writeFileSync(source,'#include <stdio.h>\nint main(void){puts("SWAPPED");return 0;}\n');compileC(source,replacement);renameSync(replacement,core);writeFileSync(release,'go');
const stdout=await new Response(running.stdout).text(),stderr=await new Response(running.stderr).text();expect(await running.exited,stderr).toBe(0);expect(stdout.trim()).toBe('VERIFIED');expect(stdout).not.toContain('SWAPPED');
});
test('an already-loaded old launcher rejects the publisher-winning core generation',async()=>{
const directory=temporary(),core=join(directory,'gstack-cso-core'),source=join(directory,'core.c'),ready=join(directory,'ready'),release=join(directory,'release');
writeFileSync(source,'#include <stdio.h>\nint main(void){puts("OLD");return 0;}\n');compileC(source,core);const oldDigest=digest(core),launcher=compileLauncher(directory,oldDigest,true);writeFileSync(join(directory,'.gstack-cso-generation'),`${oldDigest}\n`);
const running=Bun.spawn([launcher,'__cso-test-pause-before-generation-lock',ready,release],{cwd:directory,stdout:'pipe',stderr:'pipe'});await waitFor(ready);
const next=join(directory,'next-core');writeFileSync(source,'#include <stdio.h>\nint main(void){puts("NEW");return 0;}\n');compileC(source,next);const nextDigest=digest(next),manifest=join(directory,'next-generation');writeFileSync(manifest,`${nextDigest}\n`);renameSync(next,core);renameSync(manifest,join(directory,'.gstack-cso-generation'));writeFileSync(release,'go');
const stdout=await new Response(running.stdout).text(),stderr=await new Response(running.stderr).text();expect(await running.exited).toBe(69);expect(stdout).not.toContain('NEW');expect(stderr).toContain('generations do not match');
});
test('Bun retains the shared generation lock while the core runs and drops it before detached children',async()=>{
const directory=temporary(),source=join(directory,'core.ts'),core=join(directory,'gstack-cso-core'),launcher=join(directory,'gstack-cso-launcher'),locker=join(directory,'gstack-cso-publish-lock'),ready=join(directory,'ready'),release=join(directory,'release'),childReady=join(directory,'child-ready'),childRelease=join(directory,'child-release'),published=join(directory,'published');
writeFileSync(source,`import{fstatSync,writeFileSync,existsSync}from'node:fs';const fd=Number(process.env.GSTACK_CSO_GENERATION_LOCK_FD);if(!Number.isInteger(fd)||!fstatSync(fd).isDirectory())process.exit(71);writeFileSync(${JSON.stringify(ready)},'ready');while(!existsSync(${JSON.stringify(release)}))await Bun.sleep(5);const child=Bun.spawn(['/bin/sh','-c',${JSON.stringify(`touch '${childReady}'; while [ ! -f '${childRelease}' ]; do sleep .01; done`)}],{stdin:'ignore',stdout:'ignore',stderr:'ignore'});child.unref();`);
const built=spawnSync(process.execPath,['build','--compile','--no-compile-autoload-dotenv','--no-compile-autoload-bunfig','--no-compile-autoload-tsconfig','--no-compile-autoload-package-json',source,'--outfile',core],{encoding:'utf8',timeout:60_000});expect(built.status,built.stderr).toBe(0);const coreDigest=digest(core);compileLauncher(directory,coreDigest);writeFileSync(join(directory,'.gstack-cso-generation'),`${coreDigest}\n`);compileC(join(ROOT,'lib/cso/publish-lock.c'),locker);
const running=Bun.spawn([launcher],{cwd:directory,stdout:'pipe',stderr:'pipe'});await waitFor(ready);const blocked=spawnSync(locker,[directory,'/bin/sh','-c',`touch '${published}'`],{encoding:'utf8',timeout:5000});expect(blocked.status).toBe(73);expect(existsSync(published)).toBe(false);
writeFileSync(release,'go');expect(await running.exited).toBe(0);await waitFor(childReady);const admitted=spawnSync(locker,[directory,'/bin/sh','-c',`touch '${published}'`],{encoding:'utf8',timeout:5000});expect(admitted.status,admitted.stderr).toBe(0);expect(existsSync(published)).toBe(true);writeFileSync(childRelease,'go');
},70_000);
});
+65
View File
@@ -0,0 +1,65 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
const ROOT = path.resolve(import.meta.dir, '..');
const macos = process.platform === 'darwin';
const required = process.env.GSTACK_CSO_MACOS_TESTS === '1';
if (required && !macos) throw new Error('GSTACK_CSO_MACOS_TESTS=1 requires native macOS; emulation does not qualify the launcher.');
let temporary = '', marker = '', library = '';
beforeAll(() => {
if (!macos) return;
temporary = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'gstack-cso-macos-'));
marker = path.join(temporary, 'dyld-constructor-ran');
library = path.join(temporary, 'hostile.dylib');
const source = path.join(temporary, 'hostile.c');
fs.writeFileSync(source, '#include <fcntl.h>\n#include <stdlib.h>\n#include <unistd.h>\n__attribute__((constructor)) static void mark(void){const char*p=getenv("CSO_PRELOAD_MARKER");if(p){int f=open(p,O_WRONLY|O_CREAT,0600);if(f>=0)close(f);}}\n');
const built = spawnSync('/usr/bin/clang', ['-dynamiclib', source, '-o', library], { encoding: 'utf8', timeout: 30_000 });
expect(built.status).toBe(0);
}, 40_000);
afterAll(() => { if (temporary) fs.rmSync(temporary, { recursive: true, force: true }); });
describe('CSO native macOS build contract', () => {
test('macOS CI runs native signature and injection checks', () => {
const workflow = Bun.YAML.parse(fs.readFileSync(path.join(ROOT, '.github/workflows/free-tests.yml'), 'utf8')) as any;
const build = fs.readFileSync(path.join(ROOT, 'scripts/build-cso.sh'), 'utf8');
const job = workflow.jobs['cso-macos-launcher'];
expect(job['runs-on']).toBe('macos-latest');
expect(job.steps.some((step: any) => step.run === 'bun run build:cso')).toBe(true);
const gate = job.steps.find((step: any) => step.run === 'bun run test:cso:macos');
expect(gate.env.GSTACK_CSO_MACOS_TESTS).toBe('1');
expect(gate['continue-on-error']).not.toBe(true);
expect(build).toContain('-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null');
});
});
(macos ? describe : describe.skip)('CSO native macOS startup', () => {
test('the public launcher has a valid hardened-runtime signature', () => {
const launcher = path.join(ROOT, 'bin', 'gstack-cso-launcher');
const verified = spawnSync('/usr/bin/codesign', ['--verify', '--strict', launcher], { encoding: 'utf8', timeout: 30_000 });
expect(verified.status).toBe(0);
const details = spawnSync('/usr/bin/codesign', ['-d', '--verbose=4', launcher], { encoding: 'utf8', timeout: 30_000 });
expect(details.status).toBe(0);
expect(details.stderr).toMatch(/flags=.*runtime/);
const layout = spawnSync('/usr/bin/otool', ['-l', launcher], { encoding: 'utf8', timeout: 30_000 });
expect(layout.status).toBe(0);
expect(layout.stdout).toMatch(/sectname __restrict\s+segname __RESTRICT/);
});
test('DYLD constructor injection cannot run before the launcher scrubs the environment', () => {
const launcher = path.join(ROOT, 'bin', 'gstack-cso-launcher');
const result = spawnSync(launcher, ['--version'], {
cwd: temporary,
encoding: 'utf8',
timeout: 30_000,
env: { ...process.env, HOME: temporary, GSTACK_HOME: path.join(temporary, 'state'), DYLD_INSERT_LIBRARIES: library, CSO_PRELOAD_MARKER: marker },
});
expect(result.status).toBe(0);
expect(JSON.parse(result.stdout)).toEqual({ version: '3.0.0', abi: 3 });
expect(fs.existsSync(marker)).toBe(false);
});
});
+323
View File
@@ -0,0 +1,323 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { publicArchiveCacheRoot } from '../lib/cso/cache';
import { sha256, type RunReportV3, type VerificationRequest } from '../lib/cso/contracts';
import { dockerEndpoint, type DockerEndpoint } from '../lib/cso/docker';
import { patchHash } from '../lib/cso/verification';
import { qualifiedNodeCli, type QualifiedCsoCli } from './helpers/cso-qualified-cli';
const requested = process.env.GSTACK_CSO_DOCKER_TESTS === '1' && process.env.GSTACK_CSO_TEST_STACK === 'node';
const enabled = requested && Boolean(process.env.GSTACK_CSO_TEST_IMAGE);
const suite = requested ? describe : describe.skip;
let root = '';
let repo = '';
let state = '';
let endpoint: DockerEndpoint;
let cli: QualifiedCsoCli;
const application = {
actors: ['unauthenticated caller'],
assets: ['protected response body'],
entrypoints: ['GET /security'],
tenantBoundaries: ['authorization boundary'],
sensitiveOperations: ['protected response read'],
invariants: ['unauthenticated requests cannot read the protected response'],
};
const finding = {
title: 'Unauthenticated security route discloses a secret',
rootCause: 'The security route returns protected data without an authorization decision',
location: { path: 'policy.js', line: 1, symbol: 'module.exports' },
advisoryIds: [],
severity: 'high',
confidence: 'high',
confidenceRationale: 'The captured caller-to-sink trace and control review directly support the finding',
evidence: 'supported',
attackerControl: 'An unauthenticated caller can request /security',
impact: 'The response discloses protected data',
scenario: 'An unauthenticated caller requests /security and receives the protected response body',
trace: ['GET /security', 'app.js route', 'policy.js status and body'],
references: ['policy.js:1', 'app.js security route'],
recommendation: 'Require an authorization decision before returning the protected body while preserving the control route',
challenge: {
reviewer: 'lifecycle-fixture-challenger',
independent: true,
mode: 'independent_agent',
callers: 'The loopback verifier calls the route without credentials',
controls: 'No middleware or route authorization protects /security',
counterevidence: 'The legitimate /control route is separate and remains available',
conclusion: 'The captured route reaches the vulnerable policy without a protective control',
},
};
function writeSource(target: string, fixed = false): void {
fs.mkdirSync(target, { recursive: true, mode: 0o700 });
const files: Record<string, string> = {
'package.json': JSON.stringify({
name: 'cso-node-lifecycle', version: '1.0.0', private: true,
scripts: { start: 'node app.js', test: 'node --test' },
dependencies: { 'escape-html': '1.0.3' },
}) + '\n',
'package-lock.json': JSON.stringify({
name: 'cso-node-lifecycle', version: '1.0.0', lockfileVersion: 3, requires: true,
packages: {
'': { name: 'cso-node-lifecycle', version: '1.0.0', dependencies: { 'escape-html': '1.0.3' } },
'node_modules/escape-html': {
version: '1.0.3',
resolved: 'https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz',
integrity: 'sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==',
},
},
}) + '\n',
'app.js': "const http=require('node:http');const policy=require('./policy');const {control}=require('./control');const port=Number(process.env.PORT);http.createServer((req,res)=>{if(req.url==='/control'){res.writeHead(200);res.end(control());return}if(req.url==='/security'){res.writeHead(policy.status);res.end(policy.body);return}res.writeHead(404);res.end('missing')}).listen(port,'127.0.0.1');\n",
'control.js': "const escape=require('escape-html');exports.control=()=>escape('CONTROL_OK');\n",
'policy.js': fixed
? "module.exports={status:403,body:'DENIED'};\n"
: "module.exports={status:200,body:'SECRET'};\n",
'app.test.js': "const test=require('node:test');const assert=require('node:assert/strict');const {control}=require('./control');test('legitimate control remains available',()=>assert.equal(control(),'CONTROL_OK'));\n",
};
for (const [name, body] of Object.entries(files)) fs.writeFileSync(path.join(target, name), body, { mode: 0o600 });
}
function git(...args: string[]): string {
const result = spawnSync('/usr/bin/git', ['-C', repo, ...args], {
encoding: 'utf8', env: { HOME: root, PATH: '/usr/bin:/bin' }, timeout: 30_000,
});
if (result.status) throw new Error(result.stderr);
return result.stdout;
}
function runDirectory(run: Pick<RunReportV3, 'repoId' | 'runId'>): string {
return path.join(state, 'security', 'cso', run.repoId, run.runId);
}
function report(run: Pick<RunReportV3, 'repoId' | 'runId'>): RunReportV3 {
return JSON.parse(fs.readFileSync(path.join(runDirectory(run), 'report.json'), 'utf8'));
}
function writeInput(name: string, value: unknown): string {
const file = path.join(root, name);
fs.writeFileSync(file, JSON.stringify(value), { mode: 0o600 });
return file;
}
function helperOwnedCoverage(domain: string): boolean {
return ['snapshot-inputs', 'history-inputs', 'runtime-readiness'].includes(domain)
|| domain.startsWith('scanner:') || domain.startsWith('preparation:') || domain.startsWith('execution:');
}
function completeEvidence(current: RunReportV3, findings: unknown[] = []): Record<string, unknown> {
return {
application,
findings,
coverage: current.coverage.filter(item => !helperOwnedCoverage(item.domain)).map(item => ({
...item,
status: 'assessed',
method: 'fresh caller and security-boundary trace',
gaps: [],
evidence: ['current captured source'],
})),
gaps: [],
};
}
function dockerContainers(): string[] {
const result = spawnSync(endpoint.executable, ['--host', endpoint.uri, 'ps', '--all', '--quiet', '--no-trunc'], {
encoding: 'utf8', timeout: 30_000,
});
expect(result.status).toBe(0);
return result.stdout.trim().split('\n').filter(Boolean).sort();
}
function expectEmptyDirectory(directory: string): void {
expect(fs.existsSync(directory)).toBe(true);
expect(fs.readdirSync(directory).sort()).toEqual([]);
}
beforeAll(async () => {
if (!requested) return;
if (!enabled) throw new Error('Node lifecycle qualification requires GSTACK_CSO_TEST_IMAGE; a requested cold gate cannot skip');
const platform = process.arch === 'arm64' ? 'linux/arm64' : 'linux/amd64';
if (process.env.GSTACK_CSO_TEST_PLATFORM !== platform) throw new Error(`Node lifecycle qualification requires native ${platform}`);
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cso-node-lifecycle-'));
repo = path.join(root, 'repo');
state = path.join(root, 'state');
process.env.GSTACK_HOME = state;
writeSource(repo);
git('init', '-q');
git('config', 'user.email', 'fixture@example.test');
git('config', 'user.name', 'Fixture');
git('add', '.');
git('commit', '-qm', 'vulnerable fixture');
const watchdogPath = path.resolve(import.meta.dir, '../bin/gstack-cso-watchdog');
const dockerHome = path.join(root, 'docker-home');
fs.mkdirSync(dockerHome, { recursive: true, mode: 0o700 });
endpoint = await dockerEndpoint(dockerHome, {
HOME: root,
DOCKER_HOST: process.env.DOCKER_HOST ?? 'unix:///var/run/docker.sock',
});
cli = qualifiedNodeCli({
image: process.env.GSTACK_CSO_TEST_IMAGE!,
versions: JSON.parse(process.env.GSTACK_CSO_EXPECTED_VERSIONS || '{}'),
watchdogPath,
platform,
});
});
afterAll(() => {
if (root) {
fs.rmSync(root, { recursive: true, force: true });
delete process.env.GSTACK_HOME;
}
});
suite('CSO Node producer-visible repair lifecycle fixture', () => {
test('finds, reproduces, bundles, replays, and closes against current source through the command dispatcher', async () => {
const beforeContainers = dockerContainers();
let originalRunDirectory = '';
let completed = false;
try {
const original = await cli.command<RunReportV3>(['start', '--repo', repo, '--comprehensive']);
originalRunDirectory = runDirectory(original);
expect(original.coverage.find(item => item.domain === 'runtime-readiness')).toMatchObject({ status: 'assessed' });
expect(await cli.command(['submit', original.runId, writeInput('finding.json', completeEvidence(original, [finding]))])).toMatchObject({ findings: 1, completeness: 'complete' });
const submitted = report(original);
const submittedFinding = submitted.findings[0];
expect(submittedFinding).toMatchObject({ reproduction: 'not_attempted', repair: 'not_attempted', closure: 'open' });
const plan = await cli.command<any>(['runtime-plan', original.runId, 'node', '--port', '34569']);
const vulnerable = fs.readFileSync(path.join(originalRunDirectory, 'snapshot', 'policy.js'), 'utf8');
const repaired = "module.exports={status:403,body:'DENIED'};\n";
const request: VerificationRequest = {
findingId: submittedFinding.id,
runtimeProfile: cli.runtime.id,
port: 34569,
start: plan.start.command,
legitimate: [{ name: 'legitimate control', path: '/control', method: 'GET', expected: { status: 200, includes: 'CONTROL_OK' } }],
security: { name: 'unauthorized secret is denied', path: '/security', method: 'GET', expected: { status: 403, includes: 'DENIED' }, vulnerable: { status: 200, includes: 'SECRET' } },
existingTests: plan.tests.commands,
fixtures: {},
boundaryFiles: ['app.js', 'policy.js'],
testFiles: plan.tests.files,
changes: [{ path: 'policy.js', beforeSha256: sha256(vulnerable), after: repaired, effect: 'source' }],
review: {
reviewer: 'independent-lifecycle-fixture-reviewer',
independent: true,
rootCauseRepaired: true,
featurePreserved: true,
boundaryMocks: false,
rationale: 'The access decision now denies the unauthorized path while the legitimate control and original test remain intact.',
reviewedPatchHash: '',
},
};
request.review.reviewedPatchHash = patchHash(request);
const requestPath = writeInput('verification.json', request);
const review = await cli.command<any>(['record-review', original.runId, requestPath, '--producer', 'lifecycle-fixture-producer']);
request.review.artifactId = review.reviewArtifactId;
writeInput('verification.json', request);
const verified = await cli.command<any>(['verify', original.runId, requestPath]);
expect(verified).toMatchObject({
result: 'runtime_tested',
verification: {
result: 'runtime_tested',
assertionAssurance: 'authenticated_out_of_process',
testCompletionAssurance: 'self_reported',
reviewAssurance: 'self_attested',
before: { booted: true, legitimate: true, security: 'intended_failure', existingTests: true },
after: { booted: true, legitimate: true, security: 'pass', existingTests: true },
},
});
const verifiedReport = report(original);
expect(verifiedReport.findings[0]).toMatchObject({
reproduction: 'reproduced',
repair: 'runtime_tested',
verificationId: verified.verification.id,
verificationAssurance: {
assertions: 'authenticated_out_of_process',
testCompletion: 'self_reported',
review: 'self_attested',
},
});
const bundle = JSON.parse(fs.readFileSync(path.join(originalRunDirectory, verified.bundle), 'utf8'));
expect(bundle).toMatchObject({ id: verified.verification.id, requiredInputs: { runtimeImage: cli.runtime.image } });
expect(bundle.requiredInputs.dependencyClosures.before.archives).toEqual([
expect.objectContaining({
name: 'escape-html',
version: '1.0.3',
requestedHost: 'registry.npmjs.org',
declaredIntegrity: 'sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==',
}),
]);
const replayed = await cli.command<any>(['replay', verified.verification.id]);
expect(replayed).toMatchObject({ bundle: verified.verification.id, result: 'runtime_tested' });
expect(JSON.parse(fs.readFileSync(path.join(originalRunDirectory, 'replays', `${replayed.replayId}.json`), 'utf8'))).toMatchObject({
replayId: replayed.replayId,
bundleId: verified.verification.id,
verification: { result: 'runtime_tested' },
});
expect(await cli.command(['finish', original.runId])).toMatchObject({ status: 'finished', completeness: 'complete' });
fs.writeFileSync(path.join(repo, 'policy.js'), repaired, { mode: 0o600 });
git('add', 'policy.js');
git('commit', '-qm', 'repair authorization policy');
const recheck = await cli.command<any>(['recheck', submittedFinding.id, '--run', original.runId, '--repo', repo]);
const recheckRun = { repoId: original.repoId, runId: recheck.runId };
const fresh = report(recheckRun);
expect(fresh.parent).toEqual({ runId: original.runId, findingId: submittedFinding.id, kind: 'recheck' });
expect(fresh.source.originalHash).not.toBe(original.source.originalHash);
expect(report(original).findings[0]).toMatchObject({ closure: 'open', verificationId: verified.verification.id });
const freshBoundary = fs.readFileSync(path.join(runDirectory(recheckRun), 'snapshot', 'policy.js'), 'utf8');
expect(freshBoundary).toBe(repaired);
const claim = {
findingId: submittedFinding.id,
outcome: 'resolved',
evidence: [
{ kind: 'caller', path: 'policy.js', line: 1, observation: 'Fresh route trace reaches the repaired authorization decision' },
{ kind: 'security_boundary', path: 'policy.js', line: 1, observation: 'Fresh boundary source returns the denial without the protected body' },
],
rootCause: finding.rootCause,
};
expect(await cli.command(['submit', recheck.runId, writeInput('recheck.json', { ...completeEvidence(fresh), recheck: claim })])).toMatchObject({ findings: 0, completeness: 'complete' });
const retainedClaim = JSON.parse(fs.readFileSync(path.join(runDirectory(recheckRun), 'recheck-claim.json'), 'utf8'));
expect(retainedClaim.evidence).toEqual([
expect.objectContaining({ kind: 'caller', path: expect.stringMatching(/^@cso-path\/\//), sourceState: 'present', snapshotHash: fresh.source.originalHash, sourceHash: sha256(Buffer.from(repaired)), executionHash: sha256(Buffer.from(repaired)) }),
expect.objectContaining({ kind: 'security_boundary', path: expect.stringMatching(/^@cso-path\/\//), sourceState: 'present', snapshotHash: fresh.source.originalHash, sourceHash: sha256(Buffer.from(repaired)), executionHash: sha256(Buffer.from(repaired)) }),
]);
expect(await cli.command(['finish', recheck.runId])).toMatchObject({ status: 'finished', completeness: 'complete' });
expect(report(original).findings[0]).toMatchObject({
closure: 'resolved',
reproduction: 'reproduced',
repair: 'runtime_tested',
verificationId: verified.verification.id,
});
completed = true;
} finally {
expect(dockerContainers()).toEqual(beforeContainers);
if (originalRunDirectory) {
for (const directory of ['archive-staging', 'archive-materializations', 'preparation-execution', 'verification']) {
const full = path.join(originalRunDirectory, directory);
if (fs.existsSync(full)) expect(fs.readdirSync(full).sort()).toEqual([]);
}
}
}
expect(completed).toBe(true);
for (const directory of ['archive-staging', 'archive-materializations', 'preparation-execution', 'verification']) {
expectEmptyDirectory(path.join(originalRunDirectory, directory));
}
const cache = publicArchiveCacheRoot();
expect(fs.lstatSync(path.join(cache, '.lock')).isFile()).toBe(true);
expect(fs.readdirSync(path.join(cache, 'entries'))).toHaveLength(1);
expect(fs.readdirSync(path.join(cache, 'metadata'))).toHaveLength(1);
expectEmptyDirectory(path.join(cache, '.mutation-lock-leases'));
for (const directory of ['incoming', 'recovery']) expectEmptyDirectory(path.join(cache, directory));
}, 10 * 60_000);
});
+20
View File
@@ -0,0 +1,20 @@
import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { validatePostgresDatabasePolicy } from '../lib/cso/docker';
const roots:string[]=[];
afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
function policy(body:string,mode=0o444):string{const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-pg-policy-'));roots.push(root);const file=path.join(root,'databases');fs.writeFileSync(file,body,{mode});return file;}
describe('CSO PostgreSQL synthetic policy mount',()=>{
test('requires fixed-user-readable immutable permissions without weakening private policies',()=>{
expect(validatePostgresDatabasePolicy(policy('cso_primary\ncso_queue\n'))).toEqual(['cso_primary','cso_queue']);
expect(()=>validatePostgresDatabasePolicy(policy('cso_primary\n',0o600))).toThrow('public-readable');
});
test('rejects malformed, duplicate, and overlong database identities',()=>{
for(const body of ['cso_primary\ncso_primary\n','cso_bad-name\n',`cso_${'a'.repeat(49)}\n`,'cso_primary'])
expect(()=>validatePostgresDatabasePolicy(policy(body))).toThrow();
});
});
+228
View File
@@ -0,0 +1,228 @@
import { afterEach, describe, expect, test } from 'bun:test';
import { createHash } from 'node:crypto';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { PublicArchiveCache } from '../lib/cso/cache';
import { canonical, CsoError, sha256 } from '../lib/cso/contracts';
import { inspectPreparation, type CsoStack } from '../lib/cso/preparation';
import {
admitPreparationRuntime,
PreparationExecutor,
type AcquisitionReceipt,
type OfflinePreparationReceipt,
type OfflinePreparationRequest,
type PreparationAcquireRequest,
type PreparationSandboxRunner,
} from '../lib/cso/preparation-executor';
import { CSO_HELPER_ABI } from '../lib/cso/runtime-catalog';
import { completeRuntimeCatalogFixture } from './helpers/cso-runtime-catalog';
const roots: string[] = [];
const archiveBytes = Buffer.from('adversarial-public-archive');
const sha512 = (value: Buffer | string) => createHash('sha512').update(value).digest('base64');
function temporary(prefix: string): string {
const value = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
roots.push(value);
return value;
}
function writeTree(root: string, files: Record<string, string | object>): void {
for (const [relative, value] of Object.entries(files)) {
const target = path.join(root, relative);
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.writeFileSync(target, typeof value === 'string' ? value : JSON.stringify(value));
}
}
function catalog(_stack: 'node' | 'bun') { return completeRuntimeCatalogFixture('adversarial-test-v1'); }
function cacheFixture(): PublicArchiveCache {
const root = temporary('cso-adversarial-cache-');
const stagingRoot = path.join(root, 'staging');
fs.mkdirSync(stagingRoot, { mode: 0o700 });
return new PublicArchiveCache({ root: path.join(root, 'cache'), stagingRoot, maxBytes: 1024 * 1024 });
}
const qualification = {
schemaVersion: 1 as const, helperAbi: CSO_HELPER_ABI, runnerId: 'adversarial-qualified-runner',
policyVersion: 'cso-preparation-v1' as const, supportedStacks: ['node', 'bun', 'python', 'rails'] as CsoStack[],
registryRestrictionQualified: true as const, dnsRebindingTestsPassed: true as const,
acquisitionExcludesSource: true as const, offlineContainmentQualified: true as const,
immutableArchiveMounts: true as const, resourceLimitsEnforced: true as const,
};
function commandReceipts(commands: PreparationAcquireRequest['commands']) {
return commands.map((command, index) => ({
index, commandHash: sha256(canonical(command)), exitCode: 0, timedOut: false, outputTruncated: false,
}));
}
function offlineReceipt(request: OfflinePreparationRequest): OfflinePreparationReceipt {
return {
schemaVersion: 1, planHash: request.planHash, runtimeId: request.runtime.id, runtimeImage: request.runtime.image,
platform: request.runtime.platform, sourceHash: request.sourceHash, dependencyClosureHash: request.dependencyClosureHash,
configurationHash: request.configurationHash, databaseHash: request.databaseHash, deadlineEnforced: true,
network: { mode: 'none', namespaceAnchor: 'adversarial-anchor', externalEgress: false, dnsAvailable: false,
publishedPorts: false, services: ['application'] },
commands: commandReceipts(request.commands), inputSourceReadOnly: true, preparedCopySeparate: true,
archivesReadOnly: true, applicationCodeExecutedOnlyOffline: true,
};
}
class PreparedSymlinkRunner implements PreparationSandboxRunner {
readonly qualification = qualification;
constructor(private readonly linkTarget: string) {}
async acquire(): Promise<AcquisitionReceipt> { throw new Error('fixture has no public dependencies'); }
async prepareOffline(request: OfflinePreparationRequest) {
const call = temporary('cso-adversarial-prepared-');
const preparedRoot = path.join(call, 'prepared');
fs.mkdirSync(preparedRoot, { mode: 0o700 });
fs.cpSync(request.sourceRoot, preparedRoot, { recursive: true });
fs.mkdirSync(path.join(preparedRoot, 'node_modules/.bin'), { recursive: true });
fs.mkdirSync(path.join(preparedRoot, 'node_modules/lib'), { recursive: true });
fs.writeFileSync(path.join(preparedRoot, 'node_modules/lib/tool.js'), 'export default 1;\n');
fs.writeFileSync(path.join(call, 'outside.txt'), 'outside\n');
fs.symlinkSync(this.linkTarget, path.join(preparedRoot, 'node_modules/.bin/tool'));
return { preparedRoot, receipt: offlineReceipt(request) };
}
disposePrepared(preparedRoot: string): void { fs.rmSync(path.dirname(preparedRoot), { recursive: true, force: true }); }
}
function dependencyFreeNode(): string {
const root = temporary('cso-adversarial-node-');
writeTree(root, {
'package.json': { name: 'app', version: '1.0.0' },
'package-lock.json': { name: 'app', version: '1.0.0', lockfileVersion: 3,
packages: { '': { name: 'app', version: '1.0.0' } } },
});
return root;
}
async function prepareWithLink(linkTarget: string) {
const snapshot = dependencyFreeNode(), plan = inspectPreparation(snapshot, 'node');
expect(plan.status).toBe('ready');
const admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('node') });
const executor = new PreparationExecutor({ cache: cacheFixture(), runner: new PreparedSymlinkRunner(linkTarget) });
const deadline = Date.now() + 60_000;
const closure = await executor.acquire({ plan, admission, snapshot, deadline });
return executor.prepareOffline({ plan, admission, snapshot, closure, deadline });
}
class ConcurrentAcquisitionRunner implements PreparationSandboxRunner {
readonly qualification = qualification;
readonly stagingRoots: string[] = [];
private arrivals = 0;
private release!: () => void;
private readonly barrier = new Promise<void>(resolve => { this.release = resolve; });
async acquire(request: PreparationAcquireRequest): Promise<AcquisitionReceipt> {
this.stagingRoots.push(request.stagingRoot);
this.arrivals++;
if (this.arrivals === 2) this.release();
await this.barrier;
const stagingPath = 'cso-public/same-plan-0.archive';
const target = path.join(request.stagingRoot, stagingPath);
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.writeFileSync(target, archiveBytes, { flag: 'wx', mode: 0o600 });
const input = request.inputs[0].input;
const hash = sha256(archiveBytes);
const requestedUrl = input.url!;
return {
schemaVersion: 1, planHash: request.planHash, runtimeId: request.runtime.id, runtimeImage: request.runtime.image,
platform: request.runtime.platform, deadlineEnforced: true,
network: { mode: 'registry-restricted', allowedHosts: [...request.network.allowedHosts],
contactedHosts: [new URL(requestedUrl).hostname], redirectVisibility: 'opaque-tls',
dnsRebindingBlocked: true, credentialsMounted: false, sourceMounted: false, dockerSocketMounted: false },
lifecycleScriptsExecuted: false, targetCodeExecuted: false, commands: commandReceipts(request.commands),
artifacts: [{ inputIndex: request.inputs[0].index, stagingPath, installPath: 'node/0.tgz', sha256: hash,
bytes: archiveBytes.length, requestedHost: new URL(requestedUrl).hostname, requestedUrl, resolvedUrl: null,
registryResponseSha256: hash }],
};
}
async prepareOffline(): Promise<never> { throw new Error('not used'); }
disposePrepared():void{}
}
function oneDependencyNode(): string {
const root = temporary('cso-adversarial-node-dependency-');
writeTree(root, {
'package.json': { name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' } },
'package-lock.json': { name: 'app', version: '1.0.0', lockfileVersion: 3, packages: {
'': { name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' } },
'node_modules/cookie': { name: 'cookie', version: '1.0.0',
resolved: 'https://registry.npmjs.org/cookie/-/cookie-1.0.0.tgz',
integrity: `sha512-${sha512(archiveBytes)}` },
} },
});
return root;
}
afterEach(() => {
for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });
});
describe('CSO preparation adversarial regressions', () => {
test('accepts a contained relative symlink in a prepared dependency tree', async () => {
const prepared = await prepareWithLink('../lib/tool.js');
expect(prepared.preparedManifestHash).toMatch(/^[a-f0-9]{64}$/);
});
for (const target of ['../../../outside.txt', '/etc/passwd']) {
test(`rejects a prepared dependency symlink escaping through ${target.startsWith('/') ? 'an absolute target' : 'a relative target'}`, async () => {
try {
await prepareWithLink(target);
throw new Error('expected escaping prepared-tree symlink to be rejected');
} catch (error) {
expect(error).toBeInstanceOf(CsoError);
expect((error as CsoError).code).toBe('UNSAFE_PATH');
}
});
}
test('rejects conflicting duplicate Node logical lock identities', () => {
const root = temporary('cso-adversarial-node-conflict-');
writeTree(root, {
'package.json': { name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' } },
'package-lock.json': { name: 'app', version: '1.0.0', lockfileVersion: 3, packages: {
'': { name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' } },
'node_modules/cookie': { name: 'cookie', version: '1.0.0',
resolved: 'https://registry.npmjs.org/cookie/-/cookie-1.0.0.tgz', integrity: `sha512-${sha512('first')}` },
'node_modules/nested/node_modules/cookie': { name: 'cookie', version: '1.0.0',
resolved: 'https://registry.npmjs.org/cookie/-/cookie-other-1.0.0.tgz', integrity: `sha512-${sha512('second')}` },
} },
});
const plan = inspectPreparation(root, 'node');
expect(plan.status).toBe('prerequisites');
expect(plan.prerequisites[0]?.code).toBe('CONFLICTING_LOCK_IDENTITY');
expect(plan.acquisition).toEqual([]);
});
test('rejects conflicting duplicate Bun logical lock identities', () => {
const root = temporary('cso-adversarial-bun-conflict-');
writeTree(root, {
'package.json': { name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' } },
'bun.lock': { lockfileVersion: 1, workspaces: { '': { name: 'app', dependencies: { cookie: '1.0.0' } } }, packages: {
first: ['cookie@1.0.0', 'https://registry.npmjs.org/cookie/-/cookie-1.0.0.tgz', {}, `sha512-${sha512('first')}`],
second: ['cookie@1.0.0', 'https://registry.npmjs.org/cookie/-/cookie-other-1.0.0.tgz', {}, `sha512-${sha512('second')}`],
} },
});
const plan = inspectPreparation(root, 'bun');
expect(plan.status).toBe('prerequisites');
expect(plan.prerequisites[0]?.code).toBe('CONFLICTING_LOCK_IDENTITY');
expect(plan.acquisition).toEqual([]);
});
test('gives concurrent acquisitions of the same plan non-colliding staging namespaces', async () => {
const snapshot = oneDependencyNode(), plan = inspectPreparation(snapshot, 'node');
expect(plan.status).toBe('ready');
const admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('node') });
const runner = new ConcurrentAcquisitionRunner();
const executor = new PreparationExecutor({ cache: cacheFixture(), runner });
const requests = [1, 2].map(() => executor.acquire({ plan, admission, snapshot, deadline: Date.now() + 60_000 }));
const results = await Promise.allSettled(requests);
expect(results.map(result => result.status)).toEqual(['fulfilled', 'fulfilled']);
expect(new Set(runner.stagingRoots).size).toBe(2);
});
});
+63
View File
@@ -0,0 +1,63 @@
import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { createPreparedExport, recordNpmArchiveEntry } from '../lib/cso/preparation-container';
import { materializePreparedExport } from '../lib/cso/preparation-docker';
import { validateSingleContainerProcessOutput } from '../lib/cso/docker';
const roots: string[] = [];
function temporary(): string { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cso-prepared-export-')); roots.push(root); return root; }
afterEach(() => { for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); });
describe('CSO preparation container archive limits', () => {
test('a zero-byte directory flood consumes the same bounded tar-entry budget as files', () => {
let entries = 0;
for (let index = 0; index < 200_000; index++) entries = recordNpmArchiveEntry(entries, '5');
expect(entries).toBe(200_000);
expect(() => recordNpmArchiveEntry(entries, '5')).toThrow('npm archive exceeded extraction limits');
expect(() => recordNpmArchiveEntry(entries, '0')).toThrow('npm archive exceeded extraction limits');
});
test('exports only inert regular blobs and reconstructs contained dependency symlinks', () => {
const root = temporary(), work = path.join(root, 'work'), exported = path.join(work, `.gstack-cso-export-${'a'.repeat(24)}`),
inert = path.join(root, 'inert'), prepared = path.join(root, 'prepared');
fs.mkdirSync(path.join(work, 'node_modules/lib'), { recursive: true, mode: 0o700 });
fs.mkdirSync(path.join(work, 'node_modules/.bin'), { mode: 0o700 });
fs.writeFileSync(path.join(work, 'package.json'), '{"name":"fixture"}\n', { mode: 0o600 });
fs.writeFileSync(path.join(work, 'node_modules/lib/tool.js'), 'export default 1;\n', { mode: 0o500 });
fs.symlinkSync('../lib/tool.js', path.join(work, 'node_modules/.bin/tool'));
fs.mkdirSync(prepared, { mode: 0o700 });
const manifest = createPreparedExport(work, exported, 1024 * 1024);
expect(fs.readdirSync(exported).sort()).toEqual(['blobs', 'manifest.json']);
for (const name of fs.readdirSync(path.join(exported, 'blobs'))) expect(fs.lstatSync(path.join(exported, 'blobs', name)).isFile()).toBe(true);
expect(manifest.entries.some(entry => entry.kind === 'symlink')).toBe(true);
fs.cpSync(exported, inert, { recursive: true });
materializePreparedExport(inert, prepared, 1024 * 1024);
expect(fs.readFileSync(path.join(prepared, 'node_modules/.bin/tool'), 'utf8')).toBe('export default 1;\n');
expect(fs.readlinkSync(path.join(prepared, 'node_modules/.bin/tool'))).toBe('../lib/tool.js');
});
test('rejects escaping links and special lifecycle output before an export is created', () => {
for (const kind of ['symlink', 'fifo'] as const) {
if (kind === 'fifo' && process.platform === 'win32') continue;
const root = temporary(), work = path.join(root, 'work'), exported = path.join(work, `.gstack-cso-export-${(kind === 'symlink' ? 'b' : 'c').repeat(24)}`);
fs.mkdirSync(work, { mode: 0o700 });
if (kind === 'symlink') fs.symlinkSync('../../outside', path.join(work, 'host-escape'));
else {
const result = spawnSync('mkfifo', [path.join(work, 'lifecycle.fifo')], { timeout: 30_000 });
expect(result.status).toBe(0);
}
expect(() => createPreparedExport(work, exported, 1024 * 1024)).toThrow(kind === 'symlink' ? 'escaping symlink' : 'special object');
expect(fs.existsSync(exported)).toBe(false);
}
});
test('refuses acquisition or preparation export while a manager-owned background process remains', () => {
expect(() => validateSingleContainerProcessOutput('PID\n123\n')).not.toThrow();
expect(() => validateSingleContainerProcessOutput('PID\n123\n456\n')).toThrow('background process');
expect(() => validateSingleContainerProcessOutput('PID COMMAND\n123 bun\n')).toThrow('background process');
});
});
+391
View File
@@ -0,0 +1,391 @@
import { afterEach, describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import * as net from 'node:net';
import { createHash } from 'node:crypto';
import { PublicArchiveCache } from '../lib/cso/cache';
import { CsoError } from '../lib/cso/contracts';
import { inspectPreparation, type CsoStack } from '../lib/cso/preparation';
import {
admitPreparationRuntime, admitPreparationSidecar, PreparationExecutor,
type AcquisitionReceipt, type OfflinePreparationReceipt, type PreparationAcquireRequest,
type PreparationSandboxRunner, type OfflinePreparationRequest,
} from '../lib/cso/preparation-executor';
import { CSO_HELPER_ABI } from '../lib/cso/runtime-catalog';
import { DockerPreparationSandboxRunner, isBlockedRegistryAddress, RegistryEgressBroker } from '../lib/cso/preparation-docker';
import { completeRuntimeCatalogFixture, qualifiedRuntimeFixture } from './helpers/cso-runtime-catalog';
const roots: string[] = [];
const digest = 'a'.repeat(64);
const artifactBytes = (stack: CsoStack) => Buffer.from(`${stack}-verified-public-archive`);
const h256 = (value: Buffer | string) => createHash('sha256').update(value).digest('hex');
const h512 = (value: Buffer | string) => createHash('sha512').update(value).digest('base64');
function root(prefix: string): string { const value = fs.mkdtempSync(path.join(os.tmpdir(), prefix)); roots.push(value); return value; }
function writeTree(base: string, files: Record<string, string>): void {
for (const [name, content] of Object.entries(files)) {
const target = path.join(base, name); fs.mkdirSync(path.dirname(target), { recursive: true }); fs.writeFileSync(target, content);
}
}
function snapshot(stack: CsoStack): string {
const base = root(`cso-executor-${stack}-`), bytes = artifactBytes(stack);
if (stack === 'node') writeTree(base, {
'package.json': JSON.stringify({ name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' }, scripts: { postinstall: 'touch /tmp/unsafe' } }),
'package-lock.json': JSON.stringify({ name: 'app', lockfileVersion: 3, packages: {
'': { name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' } },
'node_modules/cookie': { name: 'cookie', version: '1.0.0', resolved: 'https://registry.npmjs.org/cookie/-/cookie-1.0.0.tgz', integrity: `sha512-${h512(bytes)}` },
} }),
});
if (stack === 'bun') writeTree(base, {
'package.json': JSON.stringify({ name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' } }),
'bun.lock': JSON.stringify({ lockfileVersion: 1, workspaces: { '': { name: 'app', dependencies: { cookie: '1.0.0' } } },
packages: { cookie: ['cookie@1.0.0', 'https://registry.npmjs.org/cookie/-/cookie-1.0.0.tgz', {}, `sha512-${h512(bytes)}`] } }),
});
if (stack === 'python') writeTree(base, { 'requirements.txt': `flask==3.1.0 --hash=sha256:${h256(bytes)}\n` });
if (stack === 'rails') writeTree(base, {
'Gemfile': 'source "https://rubygems.org"\ngem "rack"\ngem "sqlite3"\ngem "pg"\n',
'Gemfile.lock': `GEM\n remote: https://rubygems.org/\n specs:\n pg (1.5.9)\n rack (3.1.8)\n sqlite3 (2.5.0-x86_64-linux-gnu)\n\nPLATFORMS\n ruby\n x86_64-linux-gnu\n\nDEPENDENCIES\n pg\n rack\n sqlite3\n\nRUBY VERSION\n ruby 3.3.6p108\n\nBUNDLED WITH\n 2.6.9\n`,
});
return base;
}
function runtime(stack: CsoStack | 'postgresql') { return qualifiedRuntimeFixture(stack); }
function catalog(..._stacks: Array<CsoStack | 'postgresql'>) { return completeRuntimeCatalogFixture('executor-test-v1'); }
function cacheFixture() {
const base = root('cso-executor-cache-'), staging = path.join(base, 'staging'); fs.mkdirSync(staging, { mode: 0o700 });
return new PublicArchiveCache({ root: path.join(base, 'cache'), stagingRoot: staging, maxBytes: 1024 * 1024 });
}
// Match the helper's stable-key canonicalization without importing an internal helper.
function canonical(value: any): string {
if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`;
if (value && typeof value === 'object') return `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`;
return JSON.stringify(value);
}
function receipts(commands: PreparationAcquireRequest['commands']) {
return commands.map((command, index) => ({ index, commandHash: h256(canonical(command)), exitCode: 0, timedOut: false, outputTruncated: false }));
}
class FakeRunner implements PreparationSandboxRunner {
readonly qualification = { schemaVersion: 1 as const, helperAbi: CSO_HELPER_ABI, runnerId: 'fake-qualified-runner',
policyVersion: 'cso-preparation-v1' as const, supportedStacks: ['node', 'bun', 'python', 'rails'] as CsoStack[],
registryRestrictionQualified: true as const, dnsRebindingTestsPassed: true as const, acquisitionExcludesSource: true as const,
offlineContainmentQualified: true as const, immutableArchiveMounts: true as const, resourceLimitsEnforced: true as const };
acquireCalls = 0;
prepareCalls = 0;
acquireRequests: PreparationAcquireRequest[] = [];
offlineRequests: OfflinePreparationRequest[] = [];
mutateAcquisition?: (receipt: AcquisitionReceipt) => void;
mutatePrepared?: (preparedRoot:string) => void;
disposed:string[]=[];
beforePrepare?: (request: OfflinePreparationRequest) => void;
bytes?: Buffer;
async acquire(request: PreparationAcquireRequest): Promise<AcquisitionReceipt> {
this.acquireCalls++; this.acquireRequests.push(request);
const artifacts = [] as AcquisitionReceipt['artifacts'];
const seen = new Set<string>();
for (const { index, input } of request.inputs) {
const key = `${input.name}\0${input.version}`; if (seen.has(key)) continue; seen.add(key);
const bytes = this.bytes ?? artifactBytes(request.stack), stagingPath = `${request.stack}-${index}.archive`;
fs.writeFileSync(path.join(request.stagingRoot, stagingPath), bytes, { mode: 0o600 });
const requestedUrl = input.url ?? (request.stack === 'rails'
? `https://rubygems.org/gems/${input.name}-${input.version}.gem`
: `https://${request.network.allowedHosts[0]}/packages/${input.name}-${input.version}.archive`);
const requestedHost = new URL(requestedUrl).hostname, hash = h256(bytes);
artifacts.push({ inputIndex: index, stagingPath, installPath: `${request.stack}/${input.name}-${input.version}.archive`,
sha256: hash, bytes: bytes.length, requestedHost, requestedUrl, resolvedUrl: null, registryResponseSha256: hash });
}
const receipt: AcquisitionReceipt = { schemaVersion: 1, planHash: request.planHash, runtimeId: request.runtime.id,
runtimeImage: request.runtime.image, platform: request.runtime.platform, deadlineEnforced: true,
network: { mode: 'registry-restricted', allowedHosts: [...request.network.allowedHosts],
contactedHosts: [...new Set(artifacts.map(item => item.requestedHost))], redirectVisibility: 'opaque-tls', dnsRebindingBlocked: true,
credentialsMounted: false, sourceMounted: false, dockerSocketMounted: false },
lifecycleScriptsExecuted: false, targetCodeExecuted: false, commands: receipts(request.commands), artifacts };
this.mutateAcquisition?.(receipt);
return receipt;
}
async prepareOffline(request: OfflinePreparationRequest) {
this.prepareCalls++; this.offlineRequests.push(request);
this.beforePrepare?.(request);
for (const archive of request.archives) {
expect(fs.existsSync(archive.hostPath)).toBe(true);
expect(h256(fs.readFileSync(archive.hostPath))).toBe(archive.sha256);
}
const preparedRoot = root(`cso-prepared-${request.stack}-`);
fs.cpSync(request.sourceRoot, preparedRoot, { recursive: true });
for (const file of request.transformations) {
const target = path.join(preparedRoot, file.path); fs.mkdirSync(path.dirname(target), { recursive: true }); fs.writeFileSync(target, file.content);
}
const dependencyRoot = request.stack === 'python' ? '.venv' : request.stack === 'rails' ? 'vendor/bundle' : 'node_modules';
fs.mkdirSync(path.join(preparedRoot, dependencyRoot), { recursive: true });
fs.writeFileSync(path.join(preparedRoot, dependencyRoot, '.cso-dependencies'), request.dependencyClosureHash, { mode: 0o600 });
this.mutatePrepared?.(preparedRoot);
const services: Array<'application' | 'postgresql'> = ['application'];
const receipt: OfflinePreparationReceipt = { schemaVersion: 1, planHash: request.planHash, runtimeId: request.runtime.id,
runtimeImage: request.runtime.image, platform: request.runtime.platform, sourceHash: request.sourceHash,
dependencyClosureHash: request.dependencyClosureHash, configurationHash: request.configurationHash,
databaseHash: request.databaseHash, deadlineEnforced: true,
network: { mode: 'none', namespaceAnchor: 'fake-network-anchor', externalEgress: false, dnsAvailable: false,
publishedPorts: false, services }, commands: receipts(request.commands), inputSourceReadOnly: true,
preparedCopySeparate: true, archivesReadOnly: true, applicationCodeExecutedOnlyOffline: true };
return { preparedRoot, receipt };
}
disposePrepared(preparedRoot:string):void{this.disposed.push(preparedRoot);fs.rmSync(preparedRoot,{recursive:true,force:true});}
}
function errorCode(error: unknown): string | undefined { return error instanceof CsoError ? error.code : undefined; }
afterEach(() => { for (const value of roots.splice(0)) fs.rmSync(value, { recursive: true, force: true }); });
describe('CSO constrained dependency preparation executor', () => {
test('the unqualified default catalog and forged admissions fail closed', async () => {
const source = snapshot('node'), plan = inspectPreparation(source, 'node'), runner = new FakeRunner(), cache = cacheFixture();
expect(() => admitPreparationRuntime({ plan, platform: 'linux/amd64' })).toThrow('MISSING_QUALIFIED_RUNTIME');
const executor = new PreparationExecutor({ cache, runner });
try {
await executor.acquire({ plan, admission: { schemaVersion: 1, catalogRevision: 'forged', runtime: runtime('node') },
snapshot: source, deadline: Date.now() + 60_000 });
throw new Error('expected forged admission to fail');
} catch (error) { expect(errorCode(error)).toBe('PREREQUISITE'); }
expect(runner.acquireCalls).toBe(0);
});
for (const stack of ['node', 'bun', 'python', 'rails'] as const) test(`${stack} acquisition is registry-restricted and target setup is offline`, async () => {
const source = snapshot(stack), plan = inspectPreparation(source, stack); expect(plan.status).toBe('ready');
const admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog(stack) });
const runner = new FakeRunner(), cache = cacheFixture(), executor = new PreparationExecutor({ cache, runner });
const deadline = Date.now() + 60_000;
const closure = await executor.acquire({ plan, admission, snapshot: source, deadline });
expect(closure.archives.length).toBeGreaterThan(0);
expect(cache.get(closure.archives[0].sha256)?.bytes).toBe(artifactBytes(stack).length);
expect(runner.acquireRequests[0].sourceMounted).toBe(false);
expect('sourceRoot' in runner.acquireRequests[0]).toBe(false);
expect(runner.acquireRequests[0].network.allowedHosts).toEqual(plan.registryHosts);
const reused = await executor.acquire({ plan, admission, snapshot: source, deadline, offline: true, existingClosure: closure });
expect(reused.closureHash).toBe(closure.closureHash); expect(runner.acquireCalls).toBe(1);
const prepared = await executor.prepareOffline({ plan, admission, snapshot: source, closure, deadline,
database: stack === 'rails' ? { adapter: 'sqlite' } : undefined });
expect(prepared.preparedRoot).not.toBe(source); expect(prepared.preparedManifestHash).toMatch(/^[a-f0-9]{64}$/);expect(prepared.preparedDependencyHash).toMatch(/^[a-f0-9]{64}$/);
if (stack === 'rails') {
expect(prepared.executionEnvironment).toMatchObject({ PATH: '/usr/local/bin:/usr/bin:/bin', BUNDLE_PATH: '/work/vendor/bundle', BUNDLE_FROZEN: 'true' });
expect(prepared.executionEnvironment).not.toHaveProperty('RAILS_ENV');
expect(prepared.executionEnvironment).not.toHaveProperty('RACK_ENV');
expect(prepared.executionEnvironment).not.toHaveProperty('SECRET_KEY_BASE');
}
expect(prepared.receipt.network.mode).toBe('none'); expect(prepared.receipt.network.externalEgress).toBe(false);
expect(runner.offlineRequests[0].archives.every(item => item.containerPath.startsWith('/archives/'))).toBe(true);
});
test('offline mode names the missing closure before invoking acquisition', async () => {
const source = snapshot('python'), plan = inspectPreparation(source, 'python'), admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('python') });
const runner = new FakeRunner(), executor = new PreparationExecutor({ cache: cacheFixture(), runner });
try { await executor.acquire({ plan, admission, snapshot: source, deadline: Date.now() + 60_000, offline: true }); }
catch (error) { expect(errorCode(error)).toBe('MISSING_INPUT'); }
expect(runner.acquireCalls).toBe(0);
});
test('prepared dependency identity detects offline test-toolchain mutation',async()=>{
const source=snapshot('node'),plan=inspectPreparation(source,'node'),admission=admitPreparationRuntime({plan,platform:'linux/amd64',catalog:catalog('node')}),runner=new FakeRunner(),executor=new PreparationExecutor({cache:cacheFixture(),runner}),deadline=Date.now()+60_000,closure=await executor.acquire({plan,admission,snapshot:source,deadline});
const before=await executor.prepareOffline({plan,admission,snapshot:source,closure,deadline});runner.mutatePrepared=root=>fs.writeFileSync(path.join(root,'node_modules','.cso-dependencies'),'changed toolchain bytes');const after=await executor.prepareOffline({plan,admission,snapshot:source,closure,deadline});expect(after.preparedDependencyHash).not.toBe(before.preparedDependencyHash);
});
test('network-policy and exact-command receipt changes are rejected before promotion', async () => {
const source = snapshot('node'), plan = inspectPreparation(source, 'node'), admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('node') });
const runner = new FakeRunner(), cache = cacheFixture(), executor = new PreparationExecutor({ cache, runner });
runner.mutateAcquisition = receipt => { receipt.network.contactedHosts.push('evil.invalid'); receipt.commands[0].commandHash = digest; };
try { await executor.acquire({ plan, admission, snapshot: source, deadline: Date.now() + 60_000 }); }
catch (error) { expect(errorCode(error)).toBe('ISOLATION_FAILED'); }
expect(cache.stats().entries).toBe(0);
});
test('lock integrity is independently checked over staged bytes', async () => {
const source = snapshot('bun'), plan = inspectPreparation(source, 'bun'), admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('bun') });
const runner = new FakeRunner(); runner.bytes = Buffer.from('tampered-registry-response');
try { await new PreparationExecutor({ cache: cacheFixture(), runner }).acquire({ plan, admission, snapshot: source, deadline: Date.now() + 60_000 }); }
catch (error) { expect(errorCode(error)).toBe('INCOMPATIBLE_INPUT'); return; }
throw new Error('expected mismatched lock integrity to fail');
});
test('every offline cache hit is rehashed and poison blocks reuse', async () => {
const source = snapshot('node'), plan = inspectPreparation(source, 'node'), admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('node') });
const runner = new FakeRunner(), cache = cacheFixture(), executor = new PreparationExecutor({ cache, runner }), deadline = Date.now() + 60_000;
const closure = await executor.acquire({ plan, admission, snapshot: source, deadline });
const hit = cache.get(closure.archives[0].sha256)!; fs.chmodSync(hit.path, 0o600); fs.writeFileSync(hit.path, 'poison'); fs.chmodSync(hit.path, 0o400);
try { await executor.acquire({ plan, admission, snapshot: source, deadline, offline: true, existingClosure: closure }); }
catch (error) { expect(errorCode(error)).toBe('INCOMPATIBLE_INPUT'); return; }
throw new Error('expected poisoned cache reuse to fail');
});
test('threads cancellation into retained-closure cache verification', async () => {
const source = snapshot('node'), plan = inspectPreparation(source, 'node'), admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('node') });
const runner = new FakeRunner(), cache = cacheFixture(), executor = new PreparationExecutor({ cache, runner }), deadline = Date.now() + 60_000;
const closure = await executor.acquire({ plan, admission, snapshot: source, deadline });
const controller = new AbortController(), original = fs.readSync.bind(fs); let cacheReads = 0;
const reader = spyOn(fs, 'readSync').mockImplementation(((fd: number, buffer: NodeJS.ArrayBufferView,
offset: number, length: number, position: number | null) => {
const read = original(fd, buffer, offset, length, position);
if (length === 64 * 1024 && read > 0) { cacheReads++; controller.abort(); }
return read;
}) as typeof fs.readSync);
try {
await executor.acquire({ plan, admission, snapshot: source, deadline, signal: controller.signal, offline: true, existingClosure: closure });
throw new Error('expected retained closure verification to be cancelled');
} catch (error) { expect(errorCode(error)).toBe('CANCELLED'); }
finally { reader.mockRestore(); }
expect(cacheReads).toBe(1); expect(runner.acquireCalls).toBe(1);
});
test('cancels acquisition while independently hashing staged archive bytes', async () => {
const source = snapshot('node'), plan = inspectPreparation(source, 'node'), admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('node') });
const runner = new FakeRunner(), cache = cacheFixture(), executor = new PreparationExecutor({ cache, runner });
const controller = new AbortController(), original = fs.readSync.bind(fs); let stagedReads = 0;
const reader = spyOn(fs, 'readSync').mockImplementation(((fd: number, buffer: NodeJS.ArrayBufferView,
offset: number, length: number, position: number | null) => {
const read = original(fd, buffer, offset, length, position);
if (length === 64 * 1024 && read > 0) { stagedReads++; controller.abort(); }
return read;
}) as typeof fs.readSync);
try {
await executor.acquire({ plan, admission, snapshot: source, deadline: Date.now() + 60_000, signal: controller.signal });
throw new Error('expected staged archive verification to be cancelled');
} catch (error) { expect(errorCode(error)).toBe('CANCELLED'); }
finally { reader.mockRestore(); }
expect(stagedReads).toBe(1); expect(runner.acquireCalls).toBe(1); expect(cache.stats().entries).toBe(0);
});
test('run-owned archive copies survive a cache eviction between validation and runner start', async () => {
const source = snapshot('node'), plan = inspectPreparation(source, 'node'), admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: catalog('node') });
const runner = new FakeRunner(), base = root('cso-eviction-race-'), staging = path.join(base, 'staging'); fs.mkdirSync(staging, { mode: 0o700 });
const bytes = artifactBytes('node'), cache = new PublicArchiveCache({ root: path.join(base, 'cache'), stagingRoot: staging, maxBytes: bytes.length });
const executor = new PreparationExecutor({ cache, runner }), deadline = Date.now() + 60_000;
const closure = await executor.acquire({ plan, admission, snapshot: source, deadline });
runner.beforePrepare = request => {
expect(request.archives[0].hostPath).not.toBe(cache.get(closure.archives[0].sha256)?.path);
const replacement = Buffer.alloc(bytes.length, 0x78), replacementPath = path.join(staging, 'replacement'); fs.writeFileSync(replacementPath, replacement, { mode: 0o600 });
cache.promote('replacement', h256(replacement));
expect(cache.get(closure.archives[0].sha256)).toBeUndefined();
};
const prepared = await executor.prepareOffline({ plan, admission, snapshot: source, closure, deadline });
expect(prepared.dependencyClosureHash).toBe(closure.closureHash);
});
test('offline lifecycle source mutation fails closed and disposes the returned execution copy', async () => {
const source=snapshot('node'),plan=inspectPreparation(source,'node'),admission=admitPreparationRuntime({plan,platform:'linux/amd64',catalog:catalog('node')}),runner=new FakeRunner(),executor=new PreparationExecutor({cache:cacheFixture(),runner}),deadline=Date.now()+60_000;
const closure=await executor.acquire({plan,admission,snapshot:source,deadline});runner.mutatePrepared=prepared=>fs.writeFileSync(path.join(prepared,'package.json'),'{}');
try{await executor.prepareOffline({plan,admission,snapshot:source,closure,deadline});throw new Error('expected lifecycle source mutation to fail');}
catch(error){expect(errorCode(error)).toBe('ISOLATION_FAILED');}
expect(runner.disposed).toHaveLength(1);expect(fs.existsSync(runner.disposed[0])).toBe(false);
});
test('Rails PostgreSQL requires and records a same-catalog qualified sidecar', async () => {
const source = snapshot('rails'), plan = inspectPreparation(source, 'rails'), runtimes = catalog('rails', 'postgresql');
const admission = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: runtimes });
const sidecar = admitPreparationSidecar({ platform: 'linux/amd64', catalog: runtimes });
const runner = new FakeRunner(), executor = new PreparationExecutor({ cache: cacheFixture(), runner }), deadline = Date.now() + 60_000;
const closure = await executor.acquire({ plan, admission, snapshot: source, deadline });
const prepared = await executor.prepareOffline({ plan, admission, snapshot: source, closure, deadline, database: { adapter: 'postgresql', sidecar } });
expect(prepared.receipt.network.services).toEqual(['application']);
expect(prepared.database).toEqual({ adapter: 'postgresql', connections: ['primary'],
sidecar: { id: sidecar.runtime.id, image: sidecar.runtime.image } });
expect(prepared.databaseHash).toBe(runner.offlineRequests[0].databaseHash);
expect(runner.offlineRequests[0].database?.sidecar?.image).toContain('@sha256:');
});
test('the host registry broker rejects unallowlisted and private-address CONNECT requests', async () => {
for (const [allowed, requested] of [['registry.npmjs.org', 'evil.invalid'], ['localhost', 'localhost']] as const) {
let verified = false;
// Bun 1.3.10 can transiently report ENOENT on the first Unix-socket
// connect immediately after a verified listen when many shard processes
// are creating sockets. Retry only that runtime transient against a new
// private socket; every policy error and repeated ENOENT still fails.
for (let attempt = 0; attempt < 3 && !verified; attempt++) {
const base = root('cso-broker-'), socketPath = path.join(base, 'registry.sock');
const broker = new RegistryEgressBroker(socketPath, [allowed], Date.now() + 30_000, 1024 * 1024);
await broker.start();
try {
let reply: string;
try {
reply = await new Promise<string>((resolveReply, reject) => {
const socket = net.createConnection({ path: socketPath }); let output = '';
socket.setTimeout(5_000, () => socket.destroy(new Error('registry broker test connection timed out')));
socket.once('connect', () => socket.write(`CONNECT ${requested}:443 HTTP/1.1\r\nHost: ${requested}:443\r\n\r\n`));
socket.on('data', chunk => { output += chunk.toString(); }); socket.once('end', () => resolveReply(output)); socket.once('error', reject);
});
} catch (error: any) {
if (error?.code === 'ENOENT' && attempt < 2) continue;
throw error;
}
expect(reply).toContain('403 Forbidden');
expect(() => broker.assertClean()).toThrow();
expect(broker.contactedHosts.size).toBe(0);
verified = true;
} finally { await broker.close(); }
}
expect(verified).toBe(true);
}
});
test('registry broker close cancels and joins a pending DNS task before removing its socket', async () => {
const base = root('cso-broker-close-'), socketPath = path.join(base, 'registry.sock');
let completeLookup!: (addresses: Array<{ address: string; family: 4 | 6 }>) => void, markLookup!: () => void, cancellations = 0;
const lookupStarted = new Promise<void>(resolve => { markLookup = resolve; });
const lookup = new Promise<Array<{ address: string; family: 4 | 6 }>>(resolve => { completeLookup = resolve; });
const broker = new RegistryEgressBroker(socketPath, ['registry.npmjs.org'], Date.now() + 30_000, 1024 * 1024, () => {
markLookup(); return { promise: lookup, cancel: () => { cancellations++; } };
});
await broker.start();
const socket = net.createConnection({ path: socketPath }); socket.on('error', () => {});
const socketClosed = new Promise<void>(resolve => socket.once('close', () => resolve()));
await new Promise<void>((resolveConnect, reject) => { socket.once('connect', resolveConnect); socket.once('error', reject); });
socket.write('CONNECT registry.npmjs.org:443 HTTP/1.1\r\nHost: registry.npmjs.org:443\r\n\r\n');
await lookupStarted;
await Promise.race([broker.close(), Bun.sleep(1_000).then(() => { throw new Error('broker close did not join cancelled DNS work'); })]);
await Promise.race([socketClosed, Bun.sleep(1_000).then(() => { throw new Error('broker client socket remained open after close'); })]);
expect(cancellations).toBe(1); expect(socket.destroyed).toBe(true); expect(fs.existsSync(socketPath)).toBe(false);
completeLookup([{ address: '8.8.8.8', family: 4 }]); await Bun.sleep(10);
expect(broker.contactedHosts.size).toBe(0);
});
test('registry broker bounds DNS resolution by the connection deadline', async () => {
const base = root('cso-broker-deadline-'), socketPath = path.join(base, 'registry.sock'); let cancellations = 0, markCancelled!: () => void;
const cancelled = new Promise<void>(resolve => { markCancelled = resolve; });
const broker = new RegistryEgressBroker(socketPath, ['registry.npmjs.org'], Date.now() + 250, 1024 * 1024,
() => ({ promise: new Promise(() => {}), cancel: () => { cancellations++; markCancelled(); } }));
await broker.start();
try {
const reply = await new Promise<string>((resolveReply, reject) => {
const socket = net.createConnection({ path: socketPath }); let output = '';
socket.once('connect', () => socket.write('CONNECT registry.npmjs.org:443 HTTP/1.1\r\nHost: registry.npmjs.org:443\r\n\r\n'));
socket.on('data', chunk => { output += chunk.toString(); }); socket.once('end', () => resolveReply(output)); socket.once('error', reject);
});
expect(reply).toContain('403 Forbidden');
await Promise.race([cancelled, Bun.sleep(500).then(() => { throw new Error('registry DNS cancellation did not settle after the deadline response'); })]);
expect(cancellations).toBe(1); expect(() => broker.assertClean()).toThrow();
} finally { await broker.close(); }
});
test('registry address classification rejects mapped, translated, private, and unspecified destinations', () => {
for (const address of ['127.0.0.1', '169.254.169.254', '10.2.3.4', '100.64.0.1'])
expect(isBlockedRegistryAddress(address, 4)).toBe(true);
for (const address of ['::', '::1', '::ffff:127.0.0.1', '::ffff:169.254.169.254', '::ffff:7f00:1',
'64:ff9b::7f00:1', '64:ff9b:1::a9fe:a9fe', '100::1', '2001:db8::1', '2002:7f00:1::',
'3fff::1', 'fc00::1', 'fe80::1', 'fec0::1', 'ff02::1'])
expect(isBlockedRegistryAddress(address, 6)).toBe(true);
expect(isBlockedRegistryAddress('8.8.8.8', 4)).toBe(false);
expect(isBlockedRegistryAddress('2606:4700:4700::1111', 6)).toBe(false);
expect(isBlockedRegistryAddress('not-an-address', 4)).toBe(true);
expect(isBlockedRegistryAddress('127.0.0.1', 6)).toBe(true);
});
test('the concrete Docker runner requires the catalog-qualified container helper contract', () => {
const source = snapshot('node'), plan = inspectPreparation(source, 'node'), withoutHelper = catalog('node');
delete withoutHelper.runtimes[0].versions['cso-preparation'];
expect(() => admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: withoutHelper })).toThrow('MISSING_RUNTIME_TOOL_VERSION');
const withHelper = catalog('node');
const admitted = admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: withHelper });
const runner = new DockerPreparationSandboxRunner({ endpoint: {} as any, watchdogPath: '/missing', controlRoot: root('cso-docker-runner-'), admission: admitted });
expect(runner.qualification).toMatchObject({ runnerId: 'docker-registry-broker-v1', registryRestrictionQualified: true, offlineContainmentQualified: true });
});
});
+259
View File
@@ -0,0 +1,259 @@
import { afterEach, describe, expect, test } from 'bun:test';
import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { inspectPreparation, railsTestConfiguration } from '../lib/cso/preparation';
import { assertRuntimeCompatible, RUNTIME_CATALOG, rollbackCatalog, selectRuntime, validateRuntimeCatalog } from '../lib/cso/runtime-catalog';
import { completeRuntimeCatalogFixture, qualifiedRuntimeFixture } from './helpers/cso-runtime-catalog';
const roots: string[] = [];
const hash = 'a'.repeat(64);
const sri = `sha512-${Buffer.alloc(64, 1).toString('base64')}`;
const CREDENTIAL_ARCHIVE_URL = ['https://user:', 'secret@registry.npmjs.org/a.tgz'].join('');
function fixture(files: Record<string, unknown>): string {
const root = mkdtempSync(join(tmpdir(), 'cso-preparation-')); roots.push(root);
for (const [path, contents] of Object.entries(files)) {
const full = join(root, path); mkdirSync(join(full, '..'), { recursive: true });
writeFileSync(full, typeof contents === 'string' ? contents : JSON.stringify(contents));
}
return root;
}
afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); });
const nodeFiles = (version = 3) => ({
'package.json': { name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' }, scripts: { postinstall: 'touch /tmp/CSO_UNSAFE' } },
'package-lock.json': { name: 'app', lockfileVersion: version, packages: { '': { name: 'app', version: '1.0.0', dependencies: { cookie: '1.0.0' } }, 'node_modules/cookie': { version: '1.0.0', resolved: 'https://registry.npmjs.org/cookie/-/cookie-1.0.0.tgz', integrity: sri, hasInstallScript: true } } },
});
const gemLock = `GEM
remote: https://rubygems.org/
specs:
rack (3.1.8)
sqlite3 (2.5.0-x86_64-linux-gnu)
PLATFORMS
ruby
x86_64-linux-gnu
DEPENDENCIES
rack
sqlite3
RUBY VERSION
ruby 3.3.6p108
BUNDLED WITH
2.6.9
`;
describe('CSO inert Node preparation', () => {
for (const version of [2, 3]) test(`accepts npm lock v${version} and separates lifecycle execution`, () => {
const plan = inspectPreparation(fixture(nodeFiles(version)));
expect(plan.status).toBe('ready'); expect(plan.stack).toBe('node');
expect(plan.inputs).toHaveLength(1); expect(plan.inputs[0].integrity).toBe(sri);
expect(plan.metadata.find(m => m.path === 'package.json')!.content).not.toContain('postinstall');
expect(plan.acquisition[0].args).toContain('--ignore-scripts');
expect(plan.acquisition[0].args).toContain('--no-audit');
expect(plan.offline[0].args).toContain('--offline');
expect(plan.offline[1].args[0]).toBe('rebuild');
});
test('rejects lock v1 without emitting executable fragments', () => {
const plan = inspectPreparation(fixture(nodeFiles(1)));
expect(plan.prerequisites[0].code).toBe('UNSUPPORTED_LOCK'); expect(plan.acquisition).toEqual([]); expect(plan.metadata).toEqual([]);
});
for (const url of [CREDENTIAL_ARCHIVE_URL, 'http://registry.npmjs.org/a.tgz', 'https://registry.npmjs.org.evil.invalid/a.tgz', 'https://127.0.0.1/a.tgz', 'git+ssh://github.com/a/b', 'https://registry.npmjs.org/a.tgz?token=secret']) test(`rejects non-public archive ${url.split('@').at(-1)}`, () => {
const files = nodeFiles(); files['package-lock.json'].packages['node_modules/cookie'].resolved = url;
const plan = inspectPreparation(fixture(files)); expect(plan.status).toBe('prerequisites'); expect(plan.acquisition).toEqual([]);
});
test('rejects archive without cryptographic integrity', () => {
const files = nodeFiles(); files['package-lock.json'].packages['node_modules/cookie'].integrity = 'sha1-weak';
expect(inspectPreparation(fixture(files)).prerequisites[0].code).toBe('UNPINNED_ARCHIVE');
});
test('rejects hostile workspace paths before package-manager invocation', () => {
const files: any = nodeFiles(); files['package.json'].workspaces = ['../../host/*'];
expect(inspectPreparation(fixture(files)).prerequisites[0].code).toBe('EXTERNAL_PATH');
});
test('contains and sanitizes declared workspaces', () => {
const files: any = nodeFiles();
files['package.json'].workspaces = ['packages/*'];
files['package-lock.json'].packages['packages/local'] = { name: 'local', version: '1.0.0' };
files['package-lock.json'].packages['node_modules/local'] = { resolved: 'packages/local', link: true };
files['packages/local/package.json'] = { name: 'local', version: '1.0.0', scripts: { install: 'false' } };
const plan = inspectPreparation(fixture(files)); expect(plan.status).toBe('ready');
expect(plan.inputs.some(i => i.kind === 'local')).toBe(true);
expect(plan.metadata.find(m => m.path === 'packages/local/package.json')!.content).not.toContain('scripts');
});
test('never follows metadata symlinks', () => {
const root = fixture({ 'secret.json': nodeFiles()['package.json'] }); symlinkSync('secret.json', join(root, 'package.json'));
const plan = inspectPreparation(root); expect(plan.status).toBe('prerequisites'); expect(plan.prerequisites[0].code).toBe('UNSAFE_METADATA');
});
});
describe('CSO inert Bun preparation', () => {
test('accepts text locks with trailing commas and ignores scripts online', () => {
const plan = inspectPreparation(fixture({
'package.json': nodeFiles()['package.json'],
'bun.lock': `{"lockfileVersion":1,"workspaces":{"":{"name":"app","dependencies":{"cookie":"1.0.0",},},},"packages":{"cookie":["cookie@1.0.0","",{},"${sri}"],},}`,
'bunfig.toml': 'preload = ["./hostile.ts"]',
}));
expect(plan.status).toBe('ready'); expect(plan.stack).toBe('bun');
expect(plan.acquisition[0].args).toContain('--frozen-lockfile'); expect(plan.acquisition[0].args).toContain('--ignore-scripts');
expect(plan.acquisition[0].args).toContain('--config=/opt/cso/empty-config'); expect(plan.acquisition[0].args).toContain('--registry=https://registry.npmjs.org');
expect(plan.offline[0].args).toContain('--backend=copyfile');
expect(plan.metadata.some(m => m.path === 'bunfig.toml')).toBe(false);
});
test('does not treat lock text as JavaScript', () => {
const plan = inspectPreparation(fixture({ 'bun.lock': '({workspaces: (()=>{throw Error("EXECUTED")})()})' }));
expect(plan.prerequisites[0].code).toBe('INVALID_METADATA');
});
test('reports binary lock prerequisite', () => {
expect(inspectPreparation(fixture({ 'bun.lockb': 'binary', 'package.json': {} })).prerequisites[0].code).toBe('UNSUPPORTED_LOCK');
});
});
describe('CSO inert Python preparation', () => {
test('requires exact hashed wheels and excludes target config', () => {
const plan = inspectPreparation(fixture({ 'requirements.txt': `# generated pins\nFlask==3.1.0 \\\n --hash=sha256:${hash}\n`, 'pip.conf': '[global]\nextra-index-url=https://evil.invalid' }));
expect(plan.status).toBe('ready'); expect(plan.inputs[0].name).toBe('Flask');
expect(plan.acquisition[0].args).toContain('--only-binary=:all:'); expect(plan.acquisition[0].args).toContain('--require-hashes');
expect(plan.metadata).toHaveLength(1); expect(plan.offline[1].args).toContain('--no-index');
});
for (const requirement of ['flask>=3', '-e .', 'flask @ https://evil.invalid/x.whl', '--index-url https://evil.invalid', '-r other.txt', 'flask==3.1.0']) test(`rejects unpinned or executable requirement ${requirement}`, () => {
expect(inspectPreparation(fixture({ 'requirements.txt': requirement })).prerequisites[0].code).toBe('UNPINNED_REQUIREMENTS');
});
const uvFixture = () => ({
'uv.lock': `version = 1\nrequires-python = ">=3.12"\n[[package]]\nname = "app"\nversion = "1.0.0"\nsource = { editable = "." }\n[[package]]\nname = "flask"\nversion = "3.1.0"\nsource = { registry = "https://pypi.org/simple" }\nwheels = [{url = "https://files.pythonhosted.org/packages/flask.whl", hash = "sha256:${hash}"}]\n`,
'pyproject.toml': '[project]\nname="app"\nversion="1.0.0"\ndependencies=["flask==3.1.0"]\n[build-system]\nrequires=[]\nbuild-backend="evil_backend"\n',
});
test('uv explicitly omits first-party packages during online export', () => {
const plan = inspectPreparation(fixture(uvFixture())); expect(plan.status).toBe('ready');
expect(plan.acquisition[0].args).toContain('--no-emit-local'); expect(plan.acquisition[0].args).toContain('--frozen');
expect(plan.metadata.find(m => m.path === 'pyproject.toml')!.content).not.toContain('evil_backend');
expect(plan.offline[0].args).toContain('--offline');
expect(plan.offline.some(command => command.args.includes('--require-hashes'))).toBe(true);
expect(plan.offline.find(c => c.args.includes('--no-build-isolation'))!.args).toContain('/work/.');
expect(plan.inputs.some(i => i.kind === 'local' && i.path === '.')).toBe(true);
});
test('uv rejects local packages outside the snapshot', () => {
const files = uvFixture(); files['uv.lock'] = files['uv.lock'].replace('editable = "."', 'editable = "../private"');
expect(inspectPreparation(fixture(files)).prerequisites[0].code).toBe('EXTERNAL_PATH');
});
test('uv rejects public dependencies with only source distributions', () => {
const files = uvFixture(); files['uv.lock'] = files['uv.lock'].replace(/wheels = .*/, 'wheels = []');
expect(inspectPreparation(fixture(files)).prerequisites[0].code).toBe('MISSING_PUBLIC_WHEEL');
});
test('uv reports cross-platform marker locks as a readiness prerequisite', () => {
const files = uvFixture();
files['uv.lock'] = files['uv.lock'].replace('[[package]]\nname = "flask"', `[[package]]\nname = "colorama"\nversion = "0.4.6"\nsource = { registry = "https://pypi.org/simple" }\nwheels = [{url = "https://files.pythonhosted.org/packages/colorama.whl", hash = "sha256:${hash}"}]\ndependencies = [{ name = "win32", marker = "sys_platform == 'win32'" }]\n[[package]]\nname = "flask"`);
const plan = inspectPreparation(fixture(files));
expect(plan.status).toBe('prerequisites');
expect(plan.prerequisites.some(item => item.code === 'UNSUPPORTED_MARKER')).toBe(true);
});
test('uv requires exact public wheels for every local build dependency', () => {
const files = uvFixture(); files['pyproject.toml'] = files['pyproject.toml'].replace('requires=[]', 'requires=["setuptools>=40.8.0"]');
expect(inspectPreparation(fixture(files)).prerequisites[0].code).toBe('MISSING_BUILD_DEPENDENCY');
});
});
describe('CSO inert Rails preparation', () => {
test('does not silently choose Node in a multi-stack repository',()=>{const plan=inspectPreparation(fixture({...nodeFiles(),'Gemfile.lock':gemLock,'Gemfile':''}));expect(plan.status).toBe('prerequisites');expect(plan.prerequisites[0].code).toBe('MULTIPLE_STACKS');expect(inspectPreparation(fixture({...nodeFiles(),'Gemfile.lock':gemLock,'Gemfile':''}),'rails').stack).toBe('rails');});
test('fetches exact gems without evaluating Gemfiles or building native extensions', () => {
const plan = inspectPreparation(fixture({ 'Gemfile.lock': gemLock, 'Gemfile': 'system("touch /tmp/CSO_UNSAFE")\nsource "https://rubygems.org"' }));
expect(plan.status).toBe('ready'); expect(plan.stack).toBe('rails'); expect(plan.metadata.some(m => m.path === 'Gemfile')).toBe(false);
expect(plan.acquisition).toHaveLength(2); expect(plan.acquisition.every(c => c.args[0] === 'fetch')).toBe(true);
expect(plan.inputs[1].platform).toBe('x86_64-linux-gnu'); expect(plan.inputs[0].integritySource).toBe('registry-on-acquisition');
expect(plan.runtimeRequirements).toEqual({ ruby: '3.3.6', bundler: '2.6.9' });
expect(plan.offline[0].args).toContain('--local'); expect(plan.offline[0].env.BUNDLE_IGNORE_CONFIG).toBe('true');
expect(plan.offline[0].env.GEM_HOME).toBeUndefined(); expect(plan.offline[0].env.GEM_PATH).toBeUndefined();
});
test('records lock checksums separately from newly acquired archive hashes', () => {
const plan = inspectPreparation(fixture({ 'Gemfile.lock': gemLock + `\nCHECKSUMS\n rack (3.1.8) sha256=${hash}\n`, 'Gemfile': '' }));
expect(plan.status).toBe('ready'); expect(plan.inputs[0].integrity).toBe(`sha256:${hash}`); expect(plan.inputs[0].integritySource).toBe('lock');
});
for (const section of ['GIT', 'PATH', 'PLUGIN']) test(`rejects ${section} sources`, () => {
expect(inspectPreparation(fixture({ 'Gemfile.lock': gemLock + `\n${section}\n remote: https://evil.invalid\n`, 'Gemfile': '' })).prerequisites[0].code).toBe('UNSUPPORTED_SOURCE');
});
test('enumerates every static connection without evaluating ERB', () => {
const plan = inspectPreparation(fixture({ 'Gemfile.lock': gemLock, 'Gemfile': '', 'config/database.yml': `test:\n primary:\n adapter: sqlite3\n database: <%= ENV["DATABASE_URL"] %>\n queue:\n adapter: postgresql\nproduction:\n analytics:\n adapter: postgresql\n` }));
expect(plan.status).toBe('ready'); expect(plan.database?.connections).toEqual(['analytics', 'primary', 'queue']);
for (const adapter of ['sqlite', 'postgresql'] as const) {
const files = railsTestConfiguration(plan.database!.connections, adapter);
const database = JSON.parse(files[0].content); expect(Object.keys(database.test)).toHaveLength(3); expect(database.production).toBeUndefined();
expect(files.map(f => f.content).join('')).not.toContain('ENV["DATABASE_URL"]');
}
});
test('accepts the stock Rails default anchor without expanding arbitrary YAML aliases',()=>{
const database=`default: &default\n adapter: sqlite3\n pool: 5\ntest:\n <<: *default\n database: storage/test.sqlite3\nproduction:\n primary:\n <<: *default\n database: storage/primary.sqlite3\n queue:\n <<: *default\n database: storage/queue.sqlite3\n`;
const plan=inspectPreparation(fixture({'Gemfile.lock':gemLock,'Gemfile':'','config/database.yml':database}),'rails');
expect(plan.status).toBe('ready');expect(plan.database).toMatchObject({supported:['sqlite'],selected:'sqlite',connections:['primary','queue']});
const hostile=database.replace('primary:\n','primary: &primary\n').replace('queue:\n','queue:\n <<: *primary\n');
expect(inspectPreparation(fixture({'Gemfile.lock':gemLock,'Gemfile':'','config/database.yml':hostile}),'rails').prerequisites[0].code).toBe('DYNAMIC_DATABASE_CONFIG');
});
test('advertises and selects only database adapters actually present in the lock',()=>{
const pgLock=gemLock.replace('sqlite3 (2.5.0-x86_64-linux-gnu)','pg (1.5.9)').replace(/sqlite3/g,'pg');
const pg=inspectPreparation(fixture({'Gemfile.lock':pgLock,'Gemfile':'','config/database.yml':'test:\n adapter: postgresql\n database: app_test\n'}),'rails');
expect(pg.status).toBe('ready');expect(pg.database).toMatchObject({supported:['postgresql'],selected:'postgresql'});
const noAdapter=gemLock.replace(' sqlite3 (2.5.0-x86_64-linux-gnu)\n','').replace(' sqlite3\n','');
expect(inspectPreparation(fixture({'Gemfile.lock':noAdapter,'Gemfile':''}),'rails').prerequisites[0].code).toBe('MISSING_DATABASE_ADAPTER');
});
test('structural ERB requires explicit synthetic connection names', () => {
const plan = inspectPreparation(fixture({ 'Gemfile.lock': gemLock, 'Gemfile': '', 'config/database.yml': '<% dynamic_config %>\n' }));
expect(plan.prerequisites[0].code).toBe('DYNAMIC_DATABASE_CONFIG'); expect(plan.acquisition).toEqual([]);
});
test('rejects prototype-mutating synthetic connection names', () => {
expect(() => railsTestConfiguration(['__proto__'], 'sqlite')).toThrow('Invalid Rails connection names');
});
});
describe('CSO runtime catalog admission', () => {
const runtime = () => qualifiedRuntimeFixture('node');
const catalog = () => completeRuntimeCatalogFixture('test-1');
test('default catalog cannot execute unqualified images', () => {
expect(RUNTIME_CATALOG.profiles).toHaveLength(10);
expect(RUNTIME_CATALOG.runtimes).toEqual([]);
expect(() => selectRuntime('node', 'linux/amd64')).toThrow('Reviewed build profile node-24.4.0-amd64 is awaiting a qualified image promotion');
});
test('selects exact qualified digest and platform', () => {
expect(selectRuntime('node', 'linux/amd64', catalog()).image).toContain('@sha256:');
expect(selectRuntime('node', 'linux/arm64', catalog()).platform).toBe('linux/arm64');
expect(() => selectRuntime('missing', 'linux/arm64', catalog())).toThrow('MISSING_QUALIFIED_RUNTIME');
});
test('runtime stack and declared tool versions must match preparation',()=>{const files:any=nodeFiles();files['package.json'].engines={node:'>=24 <25'};files['package.json'].packageManager='npm@^11.0.0';const plan=inspectPreparation(fixture(files),'node'),good=runtime();expect(()=>assertRuntimeCompatible(plan,good)).not.toThrow();good.versions.node='23.9.0';expect(()=>assertRuntimeCompatible(plan,good)).toThrow('does not satisfy');const wrong={...runtime(),stack:'python' as const,versions:{python:'3.12.1',uv:'0.8.0'}};expect(()=>assertRuntimeCompatible(plan,wrong)).toThrow('cannot run');});
test('a Bun-backed node fallback cannot satisfy a declared Node engine', () => {
const plan = inspectPreparation(fixture({
'package.json': { name: 'app', version: '1.0.0', engines: { node: '>=24' } },
'bun.lock': `{"lockfileVersion":1,"workspaces":{"":{"name":"app"}},"packages":{}}`,
}), 'bun');
expect(plan.status).toBe('ready');
const bunRuntime = {
...runtime(), id: 'bun-qualified-test', stack: 'bun' as const,
versions: { bun: '1.3.10', 'cso-preparation': '1.0.0' },
};
expect(() => assertRuntimeCompatible(plan, bunRuntime)).toThrow('does not declare a real node release');
});
test('rejects tags and missing qualification evidence', () => {
const c = catalog(); c.runtimes[0].image = 'node:latest'; expect(() => validateRuntimeCatalog(c)).toThrow('UNQUALIFIED_RUNTIME');
c.runtimes[0] = runtime(); (c.runtimes[0].qualification as any).heldOutRepairPassed = false; expect(() => validateRuntimeCatalog(c)).toThrow('MISSING_APPLICATION_QUALIFICATION');
});
test('qualification evidence is exact and specific to application and PostgreSQL roles', () => {
const missingVerifierEvidence=catalog();delete (missingVerifierEvidence.runtimes[0].qualification as any).positiveNegativeAssertionsPassed;
expect(()=>validateRuntimeCatalog(missingVerifierEvidence)).toThrow('MISSING_APPLICATION_QUALIFICATION');
const application=catalog();(application.runtimes[0].qualification as any).multiDatabasePassed=true;
expect(()=>validateRuntimeCatalog(application)).toThrow('MISSING_APPLICATION_QUALIFICATION');
const valid=catalog();expect(()=>validateRuntimeCatalog(valid)).not.toThrow();
const postgresql=valid.runtimes.find(item=>item.stack==='postgresql'&&item.platform==='linux/amd64')!;
(postgresql.qualification as any).heldOutRepairPassed=true;
expect(()=>validateRuntimeCatalog(valid)).toThrow('MISSING_POSTGRESQL_QUALIFICATION');
const standaloneVerifier=catalog();Object.assign(standaloneVerifier.runtimes[0],{stack:'verifier',versions:{verifier:'1.0.0'}});
expect(()=>validateRuntimeCatalog(standaloneVerifier)).toThrow('UNSUPPORTED_RUNTIME_PLATFORM');
});
test('rejects partial nonempty catalogs and images outside the promoted gstack namespace', () => {
const partial = catalog(); partial.runtimes = [partial.runtimes[0]];
expect(() => validateRuntimeCatalog(partial)).toThrow('INCOMPLETE_QUALIFIED_RUNTIME_MATRIX');
const arbitrary = catalog(); arbitrary.runtimes[0].image = `ghcr.io/attacker/forged@sha256:${hash}`;
expect(() => validateRuntimeCatalog(arbitrary)).toThrow('UNQUALIFIED_RUNTIME');
});
test('rollback requires the previous compatible helper/catalog pair', () => {
const previous = catalog(); const current = completeRuntimeCatalogFixture('test-2', 'test-1');
expect(rollbackCatalog(current, previous)).toBe(previous);
expect(() => rollbackCatalog({ ...current, previousRevision: 'other' }, previous)).toThrow('INCOMPATIBLE_RUNTIME_ROLLBACK');
});
});
+92 -89
View File
@@ -1,107 +1,110 @@
/**
* cso security-guidance preservation test.
*
* cso carries load-bearing security prose: OWASP Top 10 mappings, STRIDE
* threat-model phrasing, mode dispatch, and false-positive-filtering exceptions
* that must NOT be auto-discarded.
*
* cso is now carved (skeleton SKILL.md + sections/audit-phases.md). The
* scope-dependent audit phases (2-11) moved to the section; the mode dispatch
* (## Arguments, ## Mode Resolution), the always-run phases (0, 1), and the
* FP-filtering exceptions (Phase 12) stay always-loaded in the skeleton.
*
* Two distinct guarantees (codex outside-voice #5 — earliest-use, not loose
* substrings):
* 1. PRESERVATION — the security phrases survive somewhere in the union
* (skeleton + sections); a carve relocates, it never drops.
* 2. ALWAYS-LOADED CONTRACT — dispatch + FP-filtering directives stay in the
* skeleton, and mode dispatch precedes any STOP-Read (a directive that
* decides which sections to read can't sit behind the STOP that reads them).
*/
/** Security-critical CSO routing/proof policy must remain in the always-loaded controller. */
import { describe, test, expect } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
const REPO_ROOT = path.resolve(import.meta.dir, '..');
const CSO_DIR = path.join(REPO_ROOT, 'cso');
const CSO_SKELETON = path.join(CSO_DIR, 'SKILL.md');
const ROOT = path.resolve(import.meta.dir, '..');
const controller = () => fs.readFileSync(path.join(ROOT, 'cso/SKILL.md'), 'utf8');
const template = () => fs.readFileSync(path.join(ROOT, 'cso/SKILL.md.tmpl'), 'utf8');
const domain = () => fs.readFileSync(path.join(ROOT, 'cso/sections/audit-phases.md'), 'utf8');
function readSkeleton(): string {
return fs.readFileSync(CSO_SKELETON, 'utf-8');
}
function readUnion(): string {
let text = readSkeleton();
const dir = path.join(CSO_DIR, 'sections');
if (fs.existsSync(dir)) {
for (const f of fs.readdirSync(dir).sort()) {
if (f.endsWith('.md') && !f.endsWith('.md.tmpl')) {
text += '\n' + fs.readFileSync(path.join(dir, f), 'utf-8');
}
describe('CSO v3 always-loaded policy', () => {
test('dispatch and execution boundaries are available before a host section read', () => {
const text = controller();
const stop = text.indexOf('> **STOP.**');
for (const directive of ['## Arguments', '## Mode Resolution', '**Private startup.**', 'untrusted evidence']) {
expect(text.indexOf(directive)).toBeGreaterThan(-1);
if (stop >= 0) expect(text.indexOf(directive)).toBeLessThan(stop);
}
}
return text;
}
// Security content that must survive the carve (checked against the UNION).
const MUST_PRESERVE_PHRASES = ['OWASP', 'STRIDE', 'daily', 'comprehensive', 'confidence', 'verif'];
describe('cso skill preserves load-bearing security guidance', () => {
test('cso skeleton exists and is non-trivial', () => {
expect(fs.existsSync(CSO_SKELETON)).toBe(true);
// Skeleton stays substantial: dispatch + always-run phases + FP filtering +
// report phases are all always-loaded. Under 30 KB means too much moved out.
expect(readSkeleton().length).toBeGreaterThan(30_000);
for (const phase of [0, 1, 12, 13, 14]) expect(text).toContain(`### Phase ${phase}:`);
});
test('security phrases survive in the union (skeleton + sections)', () => {
const union = readUnion().toLowerCase();
const missing = MUST_PRESERVE_PHRASES.filter((p) => !union.includes(p.toLowerCase()));
if (missing.length > 0) {
throw new Error(
`cso union is missing required security phrases: ${missing.join(', ')}. ` +
`These are load-bearing. A carve relocates them; it must not drop them.`,
);
test('every scope and lifecycle entrypoint survives host generation', () => {
const text = controller();
for (const flag of ['--infra', '--code', '--skills', '--supply-chain', '--owasp', '--scope', '--diff', '--base', '--budget', '--offline', '--comprehensive', '--doctor', '--resume', '--replay', '--recheck']) {
expect(text).toContain(flag);
}
expect(text).toContain('mutually exclusive');
for (const command of ['start', 'doctor', 'resume', 'replay', 'recheck', 'inspect', 'read', 'history', 'scan', 'import-sarif', 'submit', 'verify', 'finish', 'import-v2', 'inspect-v2', 'schema']) {
expect(text).toContain(`gstack-cso ${command}`);
}
});
test('ALWAYS-LOADED: mode dispatch + FP-filtering stay in the skeleton', () => {
const skeleton = readSkeleton();
// Dispatch must be always-loaded — the agent resolves scope before reading sections.
expect(skeleton).toContain('## Arguments');
expect(skeleton).toContain('## Mode Resolution');
// FP-filtering with its critical exceptions is mandatory and must not be on-demand.
expect(skeleton).toContain('Phase 12');
// The "SKILL.md files are NOT documentation" exception is a must-not-miss
// security directive (skill supply-chain findings); it stays always-loaded.
expect(skeleton).toContain('NOT documentation');
});
test('EARLIEST-USE: mode dispatch precedes any STOP-Read directive (codex #6)', () => {
const skeleton = readSkeleton();
const stop = skeleton.indexOf('> **STOP.**');
const modeRes = skeleton.indexOf('## Mode Resolution');
const args = skeleton.indexOf('## Arguments');
expect(modeRes).toBeGreaterThan(-1);
expect(args).toBeGreaterThan(-1);
if (stop >= 0) {
// A dispatch directive stranded after the STOP can't govern which sections to read.
expect(args).toBeLessThan(stop);
expect(modeRes).toBeLessThan(stop);
test('CSO cannot acquire shared preamble execution or content-export hooks', () => {
for (const forbidden of ['{{PREAMBLE}}', '{{GBRAIN_CONTEXT_LOAD}}', '{{GBRAIN_SAVE_RESULTS}}', '{{LEARNINGS_SEARCH}}', '{{LEARNINGS_LOG}}', '{{CONFIDENCE_CALIBRATION}}']) {
expect(template()).not.toContain(forbidden);
}
const text = controller();
expect(text).not.toContain('gstack-skill-start');
expect(text).not.toContain('gstack-telemetry-log');
expect(text).not.toContain('gstack-review-log');
expect(text).toContain('Do not send findings, source, secrets, harnesses, or bundles');
expect(text).toContain('trusted installed gstack distribution');
});
test('cso catalog trim landed (frontmatter description ≤ 200 chars)', () => {
const content = readSkeleton();
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
expect(fmMatch).not.toBeNull();
const desc = fmMatch![1].match(/^description:\s+(.+)$/m);
expect(desc).not.toBeNull();
expect(desc![1].trim().length).toBeLessThanOrEqual(200);
expect(desc![1]).toContain('(gstack)');
test('static review cannot acquire tested or current-source closure labels', () => {
const text = controller();
for (const proof of ['CSO evidence rubric', 'identical security assertion', 'legitimate control passes', 'pristine second copy', 'before/after configuration and dependency closures', 'boundary-replacing mocks', 'legacy review evidence', 'new evidence covering the same root cause']) {
expect(text).toContain(proof);
}
expect(text).toContain('Keep finding evidence, reproduction outcome, patch validation, test-completion assurance, review assurance, and current-source closure separate');
expect(text).toContain('cannot establish that an application booted or a repair passed tests');
});
test('cso routing prose moved to "## When to invoke" body section', () => {
expect(readSkeleton()).toContain('## When to invoke this skill');
test('runtime and scanner execution claims require qualified catalog profiles', () => {
const text = controller();
expect(text).toContain('Static assessment remains available without runtime or scanner profiles');
expect(text).toContain('matching qualified runtime catalog profile');
expect(text).toContain('Project-test completion remains `self_reported`');
expect(text).toContain('target code shares that process and can forge reporter output or terminate the runner');
expect(text).toContain('The `tested` state remains reserved until a target-independent completion witness exists');
expect(text).toContain('show assertion, test-completion, and review assurance exactly as recorded');
expect(domain()).toContain('matching qualified scanner catalog profile');
});
test('sensitive helper control files stay out of the audited working tree', () => {
const text = controller();
for (const proof of ['Private control files', 'umask 077', 'mode-`0700`', 'mode-`0600`', 'outside the audited repository', 'remove each control file immediately']) expect(text).toContain(proof);
for (const proof of ['Audited-source access invariant', "only with that run's `inspect`, `read`, and `history`", 'exact `path` from `inspect`', '`displayPath` is only a redacted label', 'Never use host `Read`/`Glob`/`Grep`', 'direct reads bypass']) expect(text).toContain(proof);
for (const proof of ["copy every record's `domain` and `scope` exactly", 'a new scope leaves the planned scope unassessed', 'Only helper commands may update helper-owned records']) expect(text).toContain(proof);
});
test('supported findings are persisted and surfaced before the final report', () => {
const text = controller();
expect(text).toContain('Invoke `start` exactly once');
expect(text).toContain('never call `start` again');
expect(text).toContain('same ID');
expect(text).toContain('submit it to the helper **and surface it to the user immediately**');
expect(text).toContain('do not wait for the final report');
expect(text).toContain('if the run is interrupted');
});
test('empty, partial, redaction failure, persistence failure and expiry stay truthful', () => {
const text = controller();
for (const contract of ['**complete**, **partial**, or **not assessed**', 'No supported findings in the assessed scope.', 'PERSISTENCE_FAILED', 'MISSING_INPUT', 'withhold the payload entirely', 'one bounded correction attempt', 'seven days', 'thirty days']) {
expect(text).toContain(contract);
}
expect(text).toContain('Completeness is independent of finding count');
expect(text).toContain('outside synchronization allowlists');
});
test('obsolete blanket exclusions and certainty scores are absent', () => {
const text = controller() + domain();
for (const obsolete of ['8/10 confidence gate', '2/10 confidence gate', 'devDependency CVEs are MEDIUM max', "gstack's own skills are trusted", 'User content in the user-message position of an AI conversation is NOT prompt injection', 'pull_request_target` without PR ref checkout is safe', 'Hard exclusions — automatically discard']) {
expect(text).not.toContain(obsolete);
}
expect(text).toContain('Unknown reachability remains');
expect(text).toContain('sequential challenge; independent agent unavailable');
});
test('controller stays compact without moving proof policy behind a section read', () => {
expect(Buffer.byteLength(template())).toBeLessThan(18_000);
expect(Buffer.byteLength(controller())).toBeLessThan(20_000);
expect(Buffer.byteLength(controller() + domain())).toBeLessThan(36_000);
const frontmatter = controller().match(/^---\n([\s\S]*?)\n---/)![1];
const description = frontmatter.match(/^description:\s+(.+)$/m)![1].trim();
expect(description.length).toBeLessThanOrEqual(200);
expect(description).toContain('(gstack)');
expect(controller()).toContain('## When to invoke this skill');
});
});
+145
View File
@@ -0,0 +1,145 @@
import { describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import {
assertPublicPackageMetadata,
parsePublicGhcrTarget,
verifyPublicGhcrImage,
type PublicGhcrDependencies,
} from '../scripts/cso-public-ghcr';
const HASH = 'a'.repeat(64);
const IMAGE = `ghcr.io/garrytan/gstack/cso-staging/node-amd64@sha256:${HASH}`;
describe('CSO public GHCR release proof', () => {
test('binds an exact CSO package and digest to its declared platform', () => {
expect(parsePublicGhcrTarget(IMAGE, 'linux/amd64', 'GarryTan/GStack')).toEqual({
image: IMAGE,
owner: 'garrytan',
repository: 'gstack',
packageName: 'gstack/cso-staging/node-amd64',
digest: `sha256:${HASH}`,
platform: 'linux/amd64',
});
expect(parsePublicGhcrTarget(
`ghcr.io/garrytan/gstack/cso-scanners/semgrep-arm64@sha256:${HASH}`,
'linux/arm64',
'garrytan/gstack',
).packageName).toBe('gstack/cso-scanners/semgrep-arm64');
});
test('rejects tags, foreign repositories, unknown packages, and platform substitution', () => {
for (const [image, platform, repository] of [
['ghcr.io/garrytan/gstack/cso-staging/node-amd64:latest', 'linux/amd64', 'garrytan/gstack'],
[IMAGE.replace('garrytan/gstack', 'attacker/gstack'), 'linux/amd64', 'garrytan/gstack'],
[IMAGE.replace('node-amd64', 'unknown-amd64'), 'linux/amd64', 'garrytan/gstack'],
[IMAGE, 'linux/arm64', 'garrytan/gstack'],
]) expect(() => parsePublicGhcrTarget(image, platform, repository)).toThrow();
});
test('accepts only exact public container metadata for the expected owner', () => {
const target = parsePublicGhcrTarget(IMAGE, 'linux/amd64', 'garrytan/gstack');
const valid = { name: target.packageName, package_type: 'container', visibility: 'public', owner: { login: 'GarryTan' } };
expect(() => assertPublicPackageMetadata(valid, target)).not.toThrow();
for (const mutation of [
{ ...valid, visibility: 'private' },
{ ...valid, name: 'gstack/cso-staging/other-amd64' },
{ ...valid, package_type: 'npm' },
{ ...valid, owner: { login: 'attacker' } },
]) expect(() => assertPublicPackageMetadata(mutation, target)).toThrow('GHCR_PACKAGE_IS_NOT_PUBLIC');
});
test('pulls through an isolated empty Docker config and strips ambient credentials', async () => {
const calls: Array<{ args: string[]; env: Record<string, string> }> = [];
const fetch = (async (url: string | URL | Request, init?: RequestInit) => {
expect((init?.headers as Record<string, string>).Authorization).toBe('Bearer metadata-token');
const value = String(url).includes('/packages/container/')
? { name: 'gstack/cso-staging/node-amd64', package_type: 'container', visibility: 'public', owner: { login: 'garrytan' } }
: { login: 'garrytan', type: 'User' };
return new Response(JSON.stringify(value), { status: 200 });
}) as typeof globalThis.fetch;
const runDocker: PublicGhcrDependencies['runDocker'] = async (args, env) => {
calls.push({ args, env });
const configRoot = args[args.indexOf('--config') + 1];
expect(fs.readFileSync(`${configRoot}/config.json`, 'utf8')).toBe('{"auths":{}}\n');
expect(env).not.toHaveProperty('GH_TOKEN');
expect(env).not.toHaveProperty('GITHUB_TOKEN');
expect(env.HOME).toBe(configRoot);
expect(env.DOCKER_CONFIG).toBe(configRoot);
if (args.includes('inspect')) return { exitCode: 0, stdout: JSON.stringify([IMAGE]), stderr: '' };
return { exitCode: 0, stdout: IMAGE, stderr: '' };
};
const proof = await verifyPublicGhcrImage(
{ image: IMAGE, platform: 'linux/amd64', githubRepository: 'garrytan/gstack', removeAfter: true },
{ fetch, githubToken: 'metadata-token', dockerPath: '/usr/bin/docker', runDocker, now: () => '2026-09-11T00:00:00.000Z', sleep: async () => {} },
);
expect(proof).toMatchObject({ image: IMAGE, packageVisibility: 'public', anonymousPull: 'passed', dockerConfig: 'isolated-empty-auths' });
expect(calls).toHaveLength(3);
expect(calls[0].args).toContain('pull');
expect(calls[0].args).toContain('--quiet');
expect(calls[2].args).toContain('rm');
});
test('fails closed before pulling a non-public package or accepting another digest', async () => {
let dockerCalls = 0;
const privateFetch = (async (url: string | URL | Request) => new Response(JSON.stringify(
String(url).includes('/packages/container/')
? { name: 'gstack/cso-staging/node-amd64', package_type: 'container', visibility: 'private', owner: { login: 'garrytan' } }
: { login: 'garrytan', type: 'User' },
), { status: 200 })) as typeof globalThis.fetch;
await expect(verifyPublicGhcrImage(
{ image: IMAGE, platform: 'linux/amd64', githubRepository: 'garrytan/gstack' },
{ fetch: privateFetch, githubToken: 'metadata-token', dockerPath: '/usr/bin/docker', runDocker: async () => { dockerCalls++; return { exitCode: 0, stdout: '', stderr: '' }; }, sleep: async () => {} },
)).rejects.toThrow('GHCR_PACKAGE_IS_NOT_PUBLIC');
expect(dockerCalls).toBe(0);
const publicFetch = (async (url: string | URL | Request) => new Response(JSON.stringify(
String(url).includes('/packages/container/')
? { name: 'gstack/cso-staging/node-amd64', package_type: 'container', visibility: 'public', owner: { login: 'garrytan' } }
: { login: 'garrytan', type: 'User' },
), { status: 200 })) as typeof globalThis.fetch;
await expect(verifyPublicGhcrImage(
{ image: IMAGE, platform: 'linux/amd64', githubRepository: 'garrytan/gstack' },
{ fetch: publicFetch, githubToken: 'metadata-token', dockerPath: '/usr/bin/docker', runDocker: async args => args.includes('inspect')
? { exitCode: 0, stdout: JSON.stringify([IMAGE.replace(HASH, 'b'.repeat(64))]), stderr: '' }
: { exitCode: 0, stdout: '', stderr: '' }, sleep: async () => {} },
)).rejects.toThrow('ANONYMOUS_IMAGE_DIGEST_MISMATCH');
});
test('requires scoped GitHub authentication for visibility metadata without passing it to Docker', async () => {
let fetched = false, pulled = false;
await expect(verifyPublicGhcrImage(
{ image: IMAGE, platform: 'linux/amd64', githubRepository: 'garrytan/gstack' },
{
githubToken: '',
dockerPath: '/usr/bin/docker',
fetch: (async () => { fetched = true; return new Response('{}'); }) as typeof globalThis.fetch,
runDocker: async () => { pulled = true; return { exitCode: 0, stdout: '', stderr: '' }; },
},
)).rejects.toThrow('GITHUB_PACKAGE_METADATA_TOKEN_REQUIRED');
expect(fetched).toBe(false);
expect(pulled).toBe(false);
});
// The public-image release verifier runs in Linux CI. This case exercises
// its process-kill path with a POSIX shebang fixture; Windows CreateProcess
// cannot execute that fixture, while the metadata contract above remains
// portable and continues to run in the curated Windows lane.
test.skipIf(process.platform === 'win32')('kills a Docker client as soon as bounded output exceeds the release limit', async () => {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'cso-public-docker-test-'));
const docker = path.join(directory, 'docker');
fs.writeFileSync(docker, '#!/bin/sh\npython3 -c "import sys; sys.stdout.write(chr(120) * 70000)"\n', { mode: 0o700 });
const fetch = (async (url: string | URL | Request) => new Response(JSON.stringify(
String(url).includes('/packages/container/')
? { name: 'gstack/cso-staging/node-amd64', package_type: 'container', visibility: 'public', owner: { login: 'garrytan' } }
: { login: 'garrytan', type: 'User' },
), { status: 200 })) as typeof globalThis.fetch;
try {
await expect(verifyPublicGhcrImage(
{ image: IMAGE, platform: 'linux/amd64', githubRepository: 'garrytan/gstack' },
{ fetch, githubToken: 'metadata-token', dockerPath: docker, sleep: async () => {} },
)).rejects.toThrow('ANONYMOUS_DOCKER_OUTPUT_LIMIT');
} finally { fs.rmSync(directory, { recursive: true, force: true }); }
});
});
+76
View File
@@ -0,0 +1,76 @@
import { expect, test } from 'bun:test';
import { spawnSync } from 'node:child_process';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { canonicalStartPlan, canonicalTestPlan, testExecutionPassed } from '../lib/cso/verification';
const python = process.platform === 'win32' ? undefined : Bun.which('python3');
function write(root:string, relative:string, body:string):void {
const file=path.join(root,relative);fs.mkdirSync(path.dirname(file),{recursive:true});fs.writeFileSync(file,body);
}
test.skipIf(!python)('Python test runners are imported before the application root without breaking app imports',()=>{
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-python-shadow-'));
try{
const venv=path.join(root,'venv'),created=spawnSync(python!,['-m','venv',venv],{encoding:'utf8',timeout:120_000});
expect(created.status,created.stderr).toBe(0);
const venvPython=path.join(venv,'bin','python'),located=spawnSync(venvPython,['-c','import sysconfig;print(sysconfig.get_paths()["purelib"])'],{encoding:'utf8',timeout:30_000});
expect(located.status,located.stderr).toBe(0);
const fakePytest=path.join(located.stdout.trim(),'pytest','__init__.py');
fs.mkdirSync(path.dirname(fakePytest),{recursive:true});
fs.writeFileSync(fakePytest,`def main(args):
import pathlib
from app import VALUE
config=pathlib.Path('.pytest.ini').read_text()
pathlib.Path('pytest-observation').write_text(repr([VALUE,args,config]))
return 0
`);
fs.writeFileSync(path.join(located.stdout.trim(),'hostile-startup.pth'),"import pathlib;pathlib.Path('pth-startup-ran').write_text('unsafe')\n");
const pytestRoot=path.join(root,'pytest-project');fs.mkdirSync(pytestRoot);
write(pytestRoot,'app.py','VALUE = 42\n');
write(pytestRoot,'.pytest.ini','[pytest]\naddopts = -q\n');
write(pytestRoot,'tests/test_app.py','from app import VALUE\ndef test_value(): assert VALUE == 42\n');
const pytestBefore=canonicalTestPlan(pytestRoot,'python');
write(pytestRoot,'pytest.py','raise RuntimeError("application shadow replaced trusted pytest")\n');
const pytestAfter=canonicalTestPlan(pytestRoot,'python');
expect(pytestAfter).toEqual(pytestBefore);
const pytestCommand=pytestBefore.commands[0];
expect(pytestCommand.executable).toBe('/work/.venv/bin/python');
expect(pytestCommand.args.slice(0,3)).toEqual(['-I','-S','-c']);
expect(pytestCommand.args[3]).toContain('import pytest;sys.path.insert');
expect(pytestCommand.args.slice(4)).toEqual(['-q','--color=no','--','./tests/test_app.py']);
const ranPytest=spawnSync(venvPython,pytestCommand.args,{cwd:pytestRoot,encoding:'utf8',timeout:30_000});
expect(ranPytest.status,ranPytest.stderr).toBe(0);
expect(fs.readFileSync(path.join(pytestRoot,'pytest-observation'),'utf8')).toContain("[42, ['-q', '--color=no', '--', './tests/test_app.py'], '[pytest]\\naddopts = -q\\n']");
expect(fs.existsSync(path.join(pytestRoot,'pth-startup-ran'))).toBe(false);
const unittestRoot=path.join(root,'unittest-project');fs.mkdirSync(unittestRoot);
write(unittestRoot,'app.py','VALUE = 42\n');
write(unittestRoot,'tests/test_app.py',`import unittest
from app import VALUE
class AppTest(unittest.TestCase):
def test_value(self): self.assertEqual(VALUE, 42)
`);
const unittestBefore=canonicalTestPlan(unittestRoot,'python');
write(unittestRoot,'unittest.py','raise RuntimeError("application shadow replaced standard unittest")\n');
const unittestAfter=canonicalTestPlan(unittestRoot,'python');
expect(unittestAfter).toEqual(unittestBefore);
const unittestCommand=unittestBefore.commands[0];
expect(unittestCommand.executable).toBe('/work/.venv/bin/python');
expect(unittestCommand.args.slice(0,3)).toEqual(['-I','-S','-c']);
expect(unittestCommand.args[3]).toContain("import os,sys,unittest;sys.path.append");
expect(unittestCommand.args.slice(4)).toEqual(['./tests/test_app.py']);
const ranUnittest=spawnSync(venvPython,unittestCommand.args,{cwd:unittestRoot,encoding:'utf8',timeout:30_000}),output=ranUnittest.stdout+ranUnittest.stderr;
expect(ranUnittest.status,output).toBe(0);
expect(output).toContain('Ran 1 test');
expect(testExecutionPassed(unittestCommand,ranUnittest.status??-1,output)).toBe(true);
expect(fs.existsSync(path.join(unittestRoot,'pth-startup-ran'))).toBe(false);
const fakeDjango=path.join(located.stdout.trim(),'django','__init__.py');fs.mkdirSync(path.dirname(fakeDjango),{recursive:true});fs.writeFileSync(fakeDjango,"ORIGIN = 'trusted-site-package'\n");
const djangoRoot=path.join(root,'django-project');fs.mkdirSync(djangoRoot);write(djangoRoot,'requirements.txt',`Django==1.0.0 --hash=sha256:${'a'.repeat(64)}\n`);write(djangoRoot,'manage.py',"import pathlib,django\npathlib.Path('django-observation').write_text(django.ORIGIN)\n");
const djangoCommand=canonicalStartPlan(djangoRoot,'python',3456).command;expect(djangoCommand.args.slice(0,3)).toEqual(['-I','-S','-c']);const ranDjango=spawnSync(venvPython,djangoCommand.args,{cwd:djangoRoot,encoding:'utf8',timeout:30_000});expect(ranDjango.status,ranDjango.stderr).toBe(0);expect(fs.readFileSync(path.join(djangoRoot,'django-observation'),'utf8')).toBe('trusted-site-package');expect(fs.existsSync(path.join(djangoRoot,'pth-startup-ran'))).toBe(false);
}finally{fs.rmSync(root,{recursive:true,force:true});}
});
+57
View File
@@ -0,0 +1,57 @@
import { afterEach, describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { DockerGroup } from '../lib/cso/docker';
import { DockerVerificationExecutor } from '../lib/cso/verification';
const roots:string[]=[];
afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
describe('CSO Rails database verification lifecycle',()=>{
test('creates fresh multi-database PostgreSQL per phase and prepares every private work tree',async()=>{
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-rails-verification-'));roots.push(root);
const source=path.join(root,'source'),work=path.join(root,'work'),control=path.join(root,'control');for(const dir of [source,work,control])fs.mkdirSync(dir);
const events:string[]=[],policies:string[][]=[];let groupNumber=0;
const create=spyOn(DockerGroup,'create').mockImplementation(async()=>{
const group=++groupNumber;let next=0;
return {
anchor:`anchor-${group}`,
createContainer:async(spec:any)=>{const id=`${group}-${spec.role}-${++next}`;events.push(`create:${id}:${spec.command.join(' ')}`);if(spec.postgresDatabasePolicy){policies.push(fs.readFileSync(spec.postgresDatabasePolicy,'utf8').trim().split('\n'));expect(fs.statSync(spec.postgresDatabasePolicy).mode&0o777).toBe(0o444);}return id;},
start:async(id:string)=>{events.push(`start:${id}`);},
execCapture:async(id:string,command:string[])=>{events.push(`exec:${id}:${command.join(' ')}`);return{code:0,stdout:'',stderr:''};},
execDetached:async(id:string,command:string[])=>{events.push(`detach:${id}:${command.join(' ')}`);},
startAttach:async(id:string)=>{events.push(`attach:${id}`);return{code:0,output:JSON.stringify({booted:true,legitimate:true,security:'pass',existingTests:false,output:'ok',inputHash:''})};},
removeContainer:async(id:string)=>{events.push(`remove:${id}`);},cleanup:async()=>{events.push(`cleanup:${group}`);},
} as any;
});
try{
const runtime:any={id:'rails',stack:'rails',image:`runtime@sha256:${'a'.repeat(64)}`,platform:'linux/amd64'};
const request:any={findingId:'b'.repeat(32),port:3456,start:{executable:'/usr/local/bin/bundle',args:['exec','rails','server']},
legitimate:[],security:{},fixtures:{},existingTests:[{executable:'/usr/local/bin/bundle',args:['exec','rspec']},{executable:'/usr/local/bin/bundle',args:['exec','rails','test']}]};
const executor=new DockerVerificationExecutor({} as any,'/watchdog',Date.now()+60_000);
const execution:any={environment:{BUNDLE_PATH:'/work/vendor/bundle'},database:{adapter:'postgresql',connections:['primary','queue'],sidecar:{id:'postgresql-17',image:`postgres@sha256:${'c'.repeat(64)}`}}};
await executor.observe(source,'before',request,runtime,runtime,work,control,execution);
await executor.observe(source,'after',request,runtime,runtime,work,control,execution);
}finally{create.mockRestore();}
expect(policies).toEqual([['cso_primary','cso_queue'],['cso_primary','cso_queue']]);
expect(events.filter(event=>event.includes('/opt/cso/postgresql-ready'))).toHaveLength(2);
expect(events.filter(event=>event.includes('/usr/local/bin/bundle exec rails db:prepare'))).toHaveLength(6);
for(const group of [1,2]){
const removeApp=events.findIndex(event=>event===`remove:${group}-app-2`),createFirstTest=events.findIndex(event=>event.startsWith(`create:${group}-tests-4:`));
expect(removeApp).toBeGreaterThan(-1);expect(createFirstTest).toBeGreaterThan(removeApp);
}
});
test('SQLite prepares the app and each test copy without a sidecar',async()=>{
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-rails-sqlite-'));roots.push(root);for(const name of ['source','work','control'])fs.mkdirSync(path.join(root,name));
const commands:string[]=[];let next=0;
const create=spyOn(DockerGroup,'create').mockResolvedValue({
createContainer:async(spec:any)=>{commands.push(`create:${spec.role}`);return`${spec.role}-${++next}`;},start:async()=>{},
execCapture:async(_id:string,command:string[])=>{commands.push(command.join(' '));return{code:0,stdout:'',stderr:''};},execDetached:async()=>{},
startAttach:async()=>({code:0,output:JSON.stringify({booted:true,legitimate:true,security:'pass',existingTests:false,output:'ok',inputHash:''})}),removeContainer:async()=>{},cleanup:async()=>{},
} as any);
try{const runtime:any={stack:'rails',image:`runtime@sha256:${'a'.repeat(64)}`},request:any={findingId:'d'.repeat(32),port:3456,start:{executable:'/usr/local/bin/bundle',args:['exec','rails','server']},legitimate:[],security:{},fixtures:{},existingTests:[{executable:'/usr/local/bin/bundle',args:['exec','rails','test']}]};await new DockerVerificationExecutor({} as any,'/watchdog',Date.now()+60_000).observe(path.join(root,'source'),'before',request,runtime,runtime,path.join(root,'work'),path.join(root,'control'),{environment:{},database:{adapter:'sqlite',connections:['primary']}});}finally{create.mockRestore();}
expect(commands.some(command=>command==='create:postgres')).toBe(false);expect(commands.filter(command=>command.includes('exec rails db:prepare'))).toHaveLength(2);
});
});
+120
View File
@@ -0,0 +1,120 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as net from 'node:net';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawn, spawnSync } from 'node:child_process';
import {
REGISTRY_SOCKET_PATH_MAX_BYTES,
RegistryEgressBroker,
superviseRegistrySocket,
type SupervisedRegistrySocket,
} from '../lib/cso/preparation-docker';
const posixDescribe = process.platform === 'win32' ? describe.skip : describe;
posixDescribe('short supervised CSO registry sockets', () => {
let fixtureRoot = '';
let watchdogPath = '';
beforeAll(() => {
fixtureRoot = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'cso-registry-socket-')));
watchdogPath = path.join(fixtureRoot, 'watchdog');
const result = spawnSync('/usr/bin/cc', ['-std=c11', '-D_POSIX_C_SOURCE=200809L', '-O2', '-Wall', '-Wextra',
path.resolve(import.meta.dir, '../lib/cso/watchdog.c'), '-o', watchdogPath], { encoding: 'utf8', timeout: 30_000 });
expect(result.status).toBe(0); expect(result.stderr).toBe('');
});
afterAll(() => { if (fixtureRoot) fs.rmSync(fixtureRoot, { recursive: true, force: true }); });
async function close(handle: SupervisedRegistrySocket | undefined): Promise<void> {
if (!handle) return;
await handle.dispose();
}
test('binds and connects through a private collision-resistant path below the Darwin limit', async () => {
const first = await superviseRegistrySocket({ watchdogPath, ownerPid: process.pid, deadline: Date.now() + 10_000 });
const second = await superviseRegistrySocket({ watchdogPath, ownerPid: process.pid, deadline: Date.now() + 10_000 });
const broker = new RegistryEgressBroker(first.socketPath, ['registry.npmjs.org'], Date.now() + 10_000, 1024 * 1024);
try {
expect(first.root).not.toBe(second.root);
expect(Buffer.byteLength(first.socketPath)).toBeLessThanOrEqual(REGISTRY_SOCKET_PATH_MAX_BYTES);
expect(Buffer.byteLength(second.socketPath)).toBeLessThanOrEqual(REGISTRY_SOCKET_PATH_MAX_BYTES);
for (const handle of [first, second]) {
expect(path.basename(handle.root)).toMatch(/^gscso-\d+-[a-f0-9]{32}$/);
const stat = fs.lstatSync(handle.root);
expect(stat.isDirectory()).toBe(true); expect(stat.isSymbolicLink()).toBe(false);
expect(stat.mode & 0o077).toBe(0); if (process.getuid) expect(stat.uid).toBe(process.getuid());
expect(fs.realpathSync(handle.root)).toBe(handle.root);
}
await broker.start();
const socketStat = fs.lstatSync(first.socketPath);
expect(socketStat.isSocket()).toBe(true); expect(socketStat.isSymbolicLink()).toBe(false);
expect(socketStat.mode & 0o077).toBe(0); if (process.getuid) expect(socketStat.uid).toBe(process.getuid());
const reply = await new Promise<string>((resolveReply, reject) => {
const socket = net.createConnection({ path: first.socketPath }); let output = '';
socket.once('connect', () => socket.write('CONNECT denied.example:443 HTTP/1.1\r\nHost: denied.example:443\r\n\r\n'));
socket.on('data', chunk => { output += chunk.toString(); }); socket.once('end', () => resolveReply(output)); socket.once('error', reject);
});
expect(reply).toContain('403 Forbidden');
} finally {
await broker.close(); await close(first); await close(second);
}
expect(fs.existsSync(first.root)).toBe(false); expect(fs.existsSync(second.root)).toBe(false);
});
test('owner-death cleanup removes the exact socket root while preserving an unrelated concurrent root', async () => {
const owner = spawn('/bin/sleep', ['30'], { stdio: 'ignore' });
const doomed = await superviseRegistrySocket({ watchdogPath, ownerPid: owner.pid!, deadline: Date.now() + 10_000 });
const survivor = await superviseRegistrySocket({ watchdogPath, ownerPid: process.pid, deadline: Date.now() + 10_000 });
const broker = new RegistryEgressBroker(doomed.socketPath, ['registry.npmjs.org'], Date.now() + 10_000, 1024 * 1024);
try {
await broker.start(); owner.kill('SIGKILL'); await new Promise(resolve => owner.once('close', resolve));
for (let attempt = 0; attempt < 50 && fs.existsSync(doomed.root); attempt++) await Bun.sleep(100);
expect(fs.existsSync(doomed.root)).toBe(false);
expect(fs.existsSync(survivor.root)).toBe(true);
} finally {
try { owner.kill('SIGKILL'); } catch {}
await broker.close(); await close(doomed); await close(survivor);
}
}, 15_000);
test('deadline cleanup removes the whole per-call socket root without a global temp sweep', async () => {
const doomed = await superviseRegistrySocket({ watchdogPath, ownerPid: process.pid, deadline: Date.now() + 150 });
const survivor = await superviseRegistrySocket({ watchdogPath, ownerPid: process.pid, deadline: Date.now() + 10_000 });
const broker = new RegistryEgressBroker(doomed.socketPath, ['registry.npmjs.org'], Date.now() + 10_000, 1024 * 1024);
try {
await broker.start(); fs.writeFileSync(path.join(doomed.root, 'owned-fixture'), 'bounded');
for (let attempt = 0; attempt < 50 && fs.existsSync(doomed.root); attempt++) await Bun.sleep(100);
expect(fs.existsSync(doomed.root)).toBe(false);
expect(fs.existsSync(survivor.root)).toBe(true);
} finally {
await broker.close(); await close(doomed); await close(survivor);
}
}, 15_000);
test('the detached cleaner refuses a same-path replacement instead of deleting it', async () => {
const base = fs.realpathSync('/tmp'), root = fs.mkdtempSync(path.join(base, 'gscso-identity-')),
control = path.join(root, 'control'), moved = `${root}.original`;
fs.chmodSync(root, 0o700); fs.mkdirSync(control, { mode: 0o700 });
const owner = spawn('/bin/sleep', ['30'], { stdio: 'ignore' });
const child = spawn(watchdogPath, ['--ephemeral-owner', String(owner.pid), '--deadline', String(Math.ceil((Date.now() + 10_000) / 1000)),
'--control-dir', control, '--work-root', root, '--run-root', base], { cwd: control, env: { PATH: '/usr/bin:/bin' }, stdio: 'ignore' });
const ownerClosed = new Promise(resolve => owner.once('close', resolve)), childClosed = new Promise(resolve => child.once('close', resolve));
try {
for (let attempt = 0; attempt < 100 && !fs.existsSync(path.join(control, 'attempt.ready')); attempt++) await Bun.sleep(10);
expect(fs.existsSync(path.join(control, 'attempt.ready'))).toBe(true);
fs.renameSync(root, moved); fs.mkdirSync(root, { mode: 0o700 }); fs.mkdirSync(control, { mode: 0o700 });
owner.kill('SIGKILL'); await ownerClosed;
const event = path.join(control, 'attempt.event');
for (let attempt = 0; attempt < 50 && !fs.existsSync(event); attempt++) await Bun.sleep(100);
expect(fs.readFileSync(event, 'utf8')).toContain('identity changed; refusing removal');
expect(fs.existsSync(root)).toBe(true); expect(fs.existsSync(moved)).toBe(true);
} finally {
try { owner.kill('SIGKILL'); } catch {} child.kill('SIGKILL');
await childClosed;
if (fs.existsSync(root)) fs.rmSync(root, { recursive: true, force: true });
if (fs.existsSync(moved)) fs.rmSync(moved, { recursive: true, force: true });
}
}, 15_000);
});
+150
View File
@@ -0,0 +1,150 @@
import { describe, expect, test } from 'bun:test';
import { createHash } from 'node:crypto';
import committedCatalog from '../lib/cso/runtime-catalog.json';
import buildInputs from '../lib/cso/images/build-inputs.json';
import { validateRuntimeCatalog } from '../lib/cso/runtime-catalog';
import { imageBuildMatrix, type ImageBuildRow } from '../scripts/cso-image-matrix';
import { catalogPromotionCandidate, validateRuntimeCatalogTransition, type RuntimeQualificationStatement } from '../scripts/cso-runtime-promotion';
import { assertVersionOutput, probesForBuildRow } from '../scripts/cso-verify-runtime-base';
const digest = (value: string) => `sha256:${createHash('sha256').update(value).digest('hex')}`;
function checks(row: ImageBuildRow): Record<string, true> {
if (row.stack === 'postgresql') return {
containmentPassed: true,
coldStartPassed: true,
multiDatabasePassed: true,
readinessPassed: true,
secretCanaryPassed: true,
watchdogCleanupPassed: true,
};
const value: Record<string, true> = {
accuracyGatesPassed: true,
acquisitionPublicOnlyPassed: true,
coldStartPassed: true,
containmentPassed: true,
heldOutRepairPassed: true,
offlineLifecyclePassed: true,
positiveNegativeAssertionsPassed: true,
secretCanaryPassed: true,
watchdogCleanupPassed: true,
};
if (row.stack === 'rails') {
value.nativeExtensionsPassed = true;
value.railsPostgresqlPassed = true;
value.railsSqlitePassed = true;
}
return value;
}
function statements(): RuntimeQualificationStatement[] {
return imageBuildMatrix(buildInputs).include.map(row => ({
schemaVersion: 1,
helperAbi: 3,
state: 'qualified',
buildRevision: row.inputRevision,
runtimeId: row.runtimeId,
stack: row.stack,
platform: row.platform,
image: `ghcr.io/garrytan/gstack/cso-staging/${row.stack}-${row.arch}@${digest(row.runtimeId)}`,
versions: row.versions,
sourceCommit: 'a'.repeat(40),
workflow: 'https://github.com/garrytan/gstack/actions/runs/123456',
qualifiedAt: '2026-09-10T12:00:00.000Z',
sbomDigest: digest(`sbom:${row.runtimeId}`),
provenanceDigest: digest(`provenance:${row.runtimeId}`),
checks: checks(row),
}));
}
describe('CSO runtime catalog promotion', () => {
test('committed build review declares both native profiles without admitting unpublished images', () => {
const matrix = imageBuildMatrix(buildInputs);
expect(matrix.include).toHaveLength(10);
expect(committedCatalog.profiles).toHaveLength(10);
expect(committedCatalog.runtimes).toEqual([]);
expect(committedCatalog.buildRevision).toBe(buildInputs.revision);
expect(matrix.include.find(row => row.stack === 'bun')!.versions).not.toHaveProperty('node');
validateRuntimeCatalog(committedCatalog);
});
test('complete same-run evidence generates a validated review candidate bound to its evidence', () => {
const candidate = catalogPromotionCandidate(committedCatalog, buildInputs, statements());
expect(candidate.previousRevision).toBe(committedCatalog.revision);
expect(candidate.runtimes).toHaveLength(10);
expect(candidate.promotion).toMatchObject({
sourceCommit: 'a'.repeat(40),
workflow: 'https://github.com/garrytan/gstack/actions/runs/123456',
});
expect(candidate.promotion!.evidenceDigest).toMatch(/^sha256:[a-f0-9]{64}$/);
expect(candidate.promotion!.qualificationEvidenceDigest).toMatch(/^sha256:[a-f0-9]{64}$/);
expect(candidate.runtimes.every(runtime => runtime.image.includes('@sha256:'))).toBe(true);
validateRuntimeCatalog(candidate);
});
test('missing release checks, mutable images, and split qualification runs cannot be promoted', () => {
const missing = statements();
delete missing[0].checks.heldOutRepairPassed;
expect(() => catalogPromotionCandidate(committedCatalog, buildInputs, missing)).toThrow('MISSING_RELEASE_GATES');
const mutable = statements();
mutable[0].image = 'ghcr.io/garrytan/gstack/cso-staging/node-amd64:latest';
expect(() => catalogPromotionCandidate(committedCatalog, buildInputs, mutable)).toThrow('INVALID_QUALIFIED_IMAGE');
const split = statements();
split[0].workflow = 'https://github.com/garrytan/gstack/actions/runs/999999';
expect(() => catalogPromotionCandidate(committedCatalog, buildInputs, split)).toThrow('SPLIT_QUALIFICATION_RUN');
const extra = statements() as Array<RuntimeQualificationStatement & { finding?: string }>;
extra[0].finding = 'must not enter a public release artifact';
expect(() => catalogPromotionCandidate(committedCatalog, buildInputs, extra)).toThrow('INVALID_QUALIFICATION_STATEMENT_FIELDS');
});
test('qualification statements must match reviewed versions and every expected platform row', () => {
const drift = statements();
drift[0].versions = { ...drift[0].versions, node: '24.5.0' };
expect(() => catalogPromotionCandidate(committedCatalog, buildInputs, drift)).toThrow('QUALIFICATION_BUILD_MISMATCH');
expect(() => catalogPromotionCandidate(committedCatalog, buildInputs, statements().slice(1))).toThrow('INCOMPLETE_QUALIFICATION_MATRIX');
});
test('promotion is a compare-and-swap against the exact reviewed catalog revision', () => {
const candidate = catalogPromotionCandidate(committedCatalog, buildInputs, statements());
expect(() => validateRuntimeCatalogTransition(committedCatalog, candidate)).not.toThrow();
expect(() => validateRuntimeCatalogTransition(
{ ...committedCatalog, revision: 'catalog-advanced-concurrently' },
candidate,
)).toThrow('RUNTIME_CATALOG_BASE_REVISION_MISMATCH');
expect(() => validateRuntimeCatalogTransition(committedCatalog, {
...candidate,
profiles: candidate.profiles.map((profile, index) => index === 0
? { ...profile, reviewedAt: '2026-09-11T00:00:00.000Z' }
: profile),
})).toThrow('RUNTIME_CATALOG_PROFILE_TRANSITION_MISMATCH');
});
test('retained promotion evidence rejects a post-generation runtime image substitution', () => {
const candidate = catalogPromotionCandidate(committedCatalog, buildInputs, statements());
const altered = structuredClone(candidate);
altered.runtimes[0].image = altered.runtimes[0].image.replace(/[a-f0-9]{64}$/, 'e'.repeat(64));
expect(altered.promotion!.evidenceDigest).toBe(candidate.promotion!.evidenceDigest);
expect(() => validateRuntimeCatalog(altered)).toThrow('RUNTIME_PROMOTION_EVIDENCE_MISMATCH');
expect(() => validateRuntimeCatalogTransition(committedCatalog, altered)).toThrow('RUNTIME_PROMOTION_EVIDENCE_MISMATCH');
});
});
describe('CSO reviewed base version probes', () => {
test('uses only real executables declared by each base profile', () => {
const rows = imageBuildMatrix(buildInputs).include.filter(row => row.platform === 'linux/amd64');
expect(probesForBuildRow(rows.find(row => row.stack === 'node')!).map(item => item.name)).toEqual(['node', 'npm']);
expect(probesForBuildRow(rows.find(row => row.stack === 'bun')!).map(item => item.name)).toEqual(['bun']);
expect(probesForBuildRow(rows.find(row => row.stack === 'python')!).map(item => item.name)).toEqual(['python', 'uv']);
expect(probesForBuildRow(rows.find(row => row.stack === 'rails')!).map(item => item.name)).toEqual(['ruby', 'bundler']);
expect(probesForBuildRow(rows.find(row => row.stack === 'postgresql')!).map(item => item.name)).toEqual(['postgresql']);
});
test('rejects a probe that reports any different release', () => {
expect(() => assertVersionOutput('node', '24.4.0', 'v24.4.0\n')).not.toThrow();
expect(() => assertVersionOutput('node', '24.4.0', 'v24.5.0\n')).toThrow('RUNTIME_VERSION_MISMATCH');
expect(() => assertVersionOutput('bundler', '2.6.7', 'Bundler version 2.6.7\n')).not.toThrow();
});
});
+73
View File
@@ -0,0 +1,73 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import { join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
import { withLock } from '../lib/cso/state';
const launcher = resolve(import.meta.dir, '../bin/gstack-cso-launcher');
let root = '', repo = '', state = '', sarif = '';
const env = () => ({ HOME: root, GSTACK_HOME: state, PATH: '/usr/bin:/bin' });
function command(args: string[]) { return spawnSync(launcher, args, { cwd: repo, env: env(), encoding: 'utf8', timeout: 30000 }); }
function start() {
const r = command(['start', '--repo', repo, '--offline']); expect(r.status).toBe(0);
const report = JSON.parse(r.stdout), dir = join(state, 'security/cso', report.repoId, report.runId);
return { runId: report.runId as string, dir, report: () => JSON.parse(fs.readFileSync(join(dir, 'report.json'), 'utf8')), artifacts: () => fs.existsSync(join(dir, 'scanner-outcomes')) ? fs.readdirSync(join(dir, 'scanner-outcomes')) : [] };
}
beforeAll(() => {
root = fs.mkdtempSync(join(fs.realpathSync(os.tmpdir()), 'cso-scanner-cli-')); repo = join(root, 'repo'); state = join(root, 'state'); fs.mkdirSync(repo);
for (const args of [['init', '-q'], ['config', 'user.email', 'test@example.test'], ['config', 'user.name', 'Test']]) { const r = spawnSync('/usr/bin/git', ['-C', repo, ...args], { env: env(), encoding: 'utf8', timeout: 30000 }); expect(r.status).toBe(0); }
fs.writeFileSync(join(repo, 'app.js'), 'export const example = true;\n');
for (const args of [['add', 'app.js'], ['commit', '-qm', 'fixture']]) expect(spawnSync('/usr/bin/git', ['-C', repo, ...args], { env: env(), encoding: 'utf8', timeout: 30000 }).status).toBe(0);
sarif = join(root, 'results.sarif'); fs.writeFileSync(sarif, JSON.stringify({ version: '2.1.0', runs: [{ tool: { driver: { name: 'CodeQL', version: '2.22.0' } }, results: [{ ruleId: 'js/test', message: { text: 'Candidate defect' }, locations: [{ physicalLocation: { artifactLocation: { uri: 'app.js' }, region: { startLine: 1 } } }] }] }] }));
});
afterAll(() => fs.rmSync(root, { recursive: true, force: true }));
describe('compiled scanner evidence persistence', () => {
test('scan uses the empty qualified catalog and records helper-owned coverage without host execution', () => {
const run = start(), r = command(['scan', run.runId, 'gitleaks']); expect(r.status).toBe(0);
const out = JSON.parse(r.stdout); expect(out.status).toBe('not_assessed'); expect(out.gaps[0].message).toContain('No qualified gitleaks');
expect(out.artifactId).toMatch(/^gitleaks-[a-f0-9]{16}-[a-f0-9]{16}$/);
expect(run.artifacts()).toEqual([`${out.artifactId}.json`]);
const stored = JSON.parse(fs.readFileSync(join(run.dir,out.artifact), 'utf8')); expect(stored.provenance.image).toBeNull(); expect(stored.outcome).not.toHaveProperty('repair');
expect(run.report().coverage.find((x: any) => x.domain === 'scanner:gitleaks')).toMatchObject({ status: 'not_assessed' });
});
test('repeated same-content SARIF imports get distinct immutable artifacts and coverage records', () => {
const run = start(), first = command(['import-sarif', run.runId, sarif]); expect(first.status).toBe(0);
const firstOut = JSON.parse(first.stdout), before = fs.readFileSync(join(run.dir,firstOut.artifact), 'utf8');
const second = command(['import-sarif', run.runId, sarif]); expect(second.status).toBe(0); const secondOut = JSON.parse(second.stdout);
expect(firstOut.artifactId).not.toBe(secondOut.artifactId); expect(run.artifacts()).toHaveLength(2); expect(fs.readFileSync(join(run.dir,firstOut.artifact), 'utf8')).toBe(before);
const coverage = run.report().coverage.filter((x: any) => x.domain === 'scanner:sarif'); expect(coverage).toHaveLength(2);
expect(coverage.every((x: any) => x.evidence.some((e: string) => e.startsWith('Immutable outcome:')))).toBe(true);
});
test('model submissions cannot erase a scanner failure or replace imported scanner evidence', () => {
const run = start(); expect(command(['scan', run.runId, 'gitleaks']).status).toBe(0);
const reportBefore = fs.readFileSync(join(run.dir, 'report.json'), 'utf8');
const file = join(root, 'scanner-coverage.json'); fs.writeFileSync(file, JSON.stringify({ coverage: [{ domain: 'scanner:gitleaks', scope: 'default', status: 'assessed', method: 'model says clean', gaps: [], exclusions: [], evidence: ['claim'] }] }));
const r = command(['submit', run.runId, file]); expect(r.status).not.toBe(0); expect(r.stderr).toContain('helper-owned'); expect(fs.readFileSync(join(run.dir, 'report.json'), 'utf8')).toBe(reportBefore);
});
test('finished reports reject scanner execution and SARIF persistence without changing artifacts', () => {
const run = start(); expect(command(['finish', run.runId]).status).toBe(0); const before = fs.readFileSync(join(run.dir, 'report.json'), 'utf8');
for (const args of [['scan', run.runId, 'gitleaks'], ['import-sarif', run.runId, sarif]]) { const r = command(args); expect(r.status).not.toBe(0); expect(r.stderr).toContain('running audit'); }
expect(run.artifacts()).toEqual([]); expect(fs.readFileSync(join(run.dir, 'report.json'), 'utf8')).toBe(before);
});
test('a live mutation lock protects both scan and SARIF writes', () => {
const run = start();
const before = fs.readFileSync(join(run.dir, 'report.json'), 'utf8');
withLock(run.dir,()=>{for (const args of [['scan', run.runId, 'gitleaks'], ['import-sarif', run.runId, sarif]]) { const r = command(args); expect(r.status).not.toBe(0); expect(r.stderr).toContain('INSUFFICIENT_CAPACITY'); }});
expect(run.artifacts()).toEqual([]); expect(fs.readFileSync(join(run.dir, 'report.json'), 'utf8')).toBe(before);
});
test('concurrent imports preserve every successful artifact and never lose a coverage update', async () => {
const run = start();
const attempt = async () => { const child = Bun.spawn([launcher, 'import-sarif', run.runId, sarif], { cwd: repo, env: env(), stdout: 'pipe', stderr: 'pipe' }); const [code, stdout, stderr] = await Promise.all([child.exited, new Response(child.stdout).text(), new Response(child.stderr).text()]); return { code, stdout, stderr }; };
const results = await Promise.all([attempt(), attempt()]), successes = results.filter(r => r.code === 0);
expect(successes.length).toBeGreaterThan(0); for (const failure of results.filter(r => r.code !== 0)) expect(failure.stderr).toContain('INSUFFICIENT_CAPACITY');
expect(run.artifacts()).toHaveLength(successes.length); expect(run.report().coverage.filter((c: any) => c.domain === 'scanner:sarif')).toHaveLength(successes.length);
expect(new Set(successes.map(r => JSON.parse(r.stdout).artifactId)).size).toBe(successes.length);
});
test('malformed SARIF remains a persisted coverage failure', () => {
const run = start(), malformed = join(root, 'broken.sarif'); fs.writeFileSync(malformed, '{');
const r = command(['import-sarif', run.runId, malformed]); expect(r.status).toBe(0); expect(JSON.parse(r.stdout).status).toBe('not_assessed');
expect(run.report().coverage.find((c: any) => c.domain === 'scanner:sarif')).toMatchObject({ status: 'not_assessed' });
});
});
+122
View File
@@ -0,0 +1,122 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { canonical, sha256 } from '../lib/cso/contracts';
import { dockerEndpoint, ISOLATION_POLICY_HASH } from '../lib/cso/docker';
import { createDockerScannerRunner, ScannerRunInput } from '../lib/cso/scanner-executor';
import { QualifiedRuntime } from '../lib/cso/runtime-catalog';
import { assertScannerVersionOutput, QualifiedScanner, scannerVersionHash } from '../lib/cso/scanner-catalog';
import { ScannerId, parseScannerOutput, scannerPlans } from '../lib/cso/scanners';
import { secureDirectory } from '../lib/cso/state';
const requested = process.env.GSTACK_CSO_SCANNER_DOCKER_TESTS === '1';
const suite = requested ? describe : describe.skip;
const HASH = 'a'.repeat(64), DIGEST = `sha256:${HASH}`;
let root = '', source = '', watchdog = '', staged: any;
describe('scanner qualification version evidence',()=>{
test.each([
['gitleaks','gitleaks version 8.30.1','8.30.1'],
['osv','osv-scanner version: 2.4.0','2.4.0'],
['semgrep','1.136.0','1.136.0'],
['zizmor','zizmor 1.11.2','1.11.2'],
['trivy','Version: 0.67.2','0.67.2'],
['schemathesis','schemathesis, version 4.5.2','4.5.2'],
] as const)('accepts a supported real version layout: %s %s',(scanner,output,version)=>{
expect(()=>assertScannerVersionOutput(scanner,version,`${output}\n`)).not.toThrow();
});
test.each(['11.2.3','1.2.30','1.2.3-dev','prefix1.2.3','1.2.3suffix'])('rejects a substring version match: %s',output=>{
expect(()=>assertScannerVersionOutput('gitleaks','1.2.3',output)).toThrow('exact catalog version');
});
test('rejects an expected version that appears only in secondary metadata',()=>{
expect(()=>assertScannerVersionOutput('gitleaks','1.2.3','gitleaks version 9.9.9\nruntime 1.2.3\n')).toThrow('primary version');
});
test('uses the same 8192-byte output boundary as production execution',()=>{
const prefix='gitleaks version 1.2.3\n',within=prefix+'x'.repeat(8192-Buffer.byteLength(prefix));
expect(()=>assertScannerVersionOutput('gitleaks','1.2.3',within)).not.toThrow();
expect(()=>assertScannerVersionOutput('gitleaks','1.2.3',within+'x')).toThrow('bounded output');
});
});
function write(name: string, body: string): void {
const target = path.join(source, name); fs.mkdirSync(path.dirname(target), { recursive: true }); fs.writeFileSync(target, body, { mode: 0o600 });
}
function fixture(): void {
write('app.js', 'export const scannerQualification = true;\n');
write('package.json', '{"name":"cso-scanner-qualification","version":"1.0.0"}\n');
write('package-lock.json', '{"name":"cso-scanner-qualification","version":"1.0.0","lockfileVersion":3,"packages":{"":{"name":"cso-scanner-qualification","version":"1.0.0"}}}\n');
write('.github/workflows/qualification.yml', 'name: qualification\non: [push]\npermissions: {}\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo qualification\n');
write('server.py', [
'from http.server import BaseHTTPRequestHandler, HTTPServer',
'class H(BaseHTTPRequestHandler):',
' def do_GET(self):',
" body = b'CONTROL_OK' if self.path == '/control' else b'[]'",
' self.send_response(200)',
" self.send_header('Content-Type', 'application/json')",
" self.send_header('Content-Length', str(len(body)))",
' self.end_headers(); self.wfile.write(body)',
' def log_message(self, *args): pass',
"HTTPServer(('127.0.0.1', 34620), H).serve_forever()",
].join('\n') + '\n');
}
function request() {
return { api: { runtimeProfile: 'qualification-runtime', port: 34620,
start: { executable: staged.applicationExecutable, args: ['server.py'] },
control: { name: 'qualification control', path: '/control', method: 'GET' as const, expected: { status: 200, includes: 'CONTROL_OK' } },
boundaryFiles: ['server.py'], operationIds: ['listItems'],
schema: { openapi: '3.1.0', info: { title: 'CSO scanner qualification', version: '1.0.0' }, paths: { '/items': { get: { operationId: 'listItems', responses: { '200': { description: 'items' } } } } } } } };
}
beforeAll(async () => {
if (!requested) return;
const file = process.env.GSTACK_CSO_SCANNER_PROFILE;
if (!file) throw new Error('Scanner Docker qualification requires GSTACK_CSO_SCANNER_PROFILE');
staged = JSON.parse(fs.readFileSync(file, 'utf8'));
if (!['gitleaks', 'osv', 'semgrep', 'zizmor', 'trivy', 'schemathesis'].includes(staged.scanner) || !/^.+@sha256:[a-f0-9]{64}$/.test(staged.image) || !/^[0-9][A-Za-z0-9.+_-]{0,100}$/.test(staged.version) || !Array.isArray(staged.capabilities)) throw new Error('Scanner Docker qualification profile is invalid');
if (staged.isolationPolicyHash !== ISOLATION_POLICY_HASH) throw new Error('Scanner Docker qualification profile does not match the helper isolation policy');
if (!process.env.GSTACK_CSO_SCANNER_VERSION_HASH) throw new Error('Scanner Docker qualification requires GSTACK_CSO_SCANNER_VERSION_HASH');
const platform = process.arch === 'arm64' ? 'linux/arm64' : 'linux/amd64';
if (staged.platform !== platform) throw new Error(`Scanner Docker qualification requires native ${platform}`);
if (staged.scanner === 'schemathesis' && (typeof staged.applicationExecutable !== 'string' || !staged.applicationExecutable.startsWith('/'))) throw new Error('Schemathesis qualification requires its reviewed fixture runtime executable');
root = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'cso-scanner-docker-'));
source = secureDirectory(path.join(root, 'source')); fixture();
process.env.GSTACK_HOME = path.join(root, 'state');
watchdog = path.resolve(import.meta.dir, '../bin/gstack-cso-watchdog');
if (!fs.existsSync(watchdog)) throw new Error('Scanner Docker qualification requires the compiled watchdog');
await dockerEndpoint(secureDirectory(path.join(root, 'docker-home')), { HOME: root, DOCKER_HOST: process.env.DOCKER_HOST ?? 'unix:///var/run/docker.sock' });
});
afterAll(() => { if (root) fs.rmSync(root, { recursive: true, force: true }); delete process.env.GSTACK_HOME; });
suite('qualified CSO scanner image', () => {
test('runs the exact adapter through DockerGroup with network-none inputs and bounded normalization', async () => {
const id = staged.scanner as ScannerId, api = id === 'schemathesis' ? request().api : undefined;
const plan = scannerPlans({ snapshotRoot: '/source', offline: true, selected: [id], deadlineSeconds: 120,
tools: { [id]: { available: true, version: staged.version, capabilities: staged.capabilities } },
semgrepRules: staged.assets?.semgrepRules?.path, advisoryCache: staged.assets?.advisoryDatabase?.path,
...(api ? { schemaPath: '/policy/openapi.json', baseUrl: `http://127.0.0.1:${api.port}/`, operationIds: api.operationIds, seed: 1, maxExamples: 3 } : {}) })[0];
expect(plan.prerequisites).toEqual([]);
expect(plan.network).toBe(id === 'schemathesis' ? 'loopback' : 'none');
const profile: QualifiedScanner = { id: `qualification-${id}`, scanner: id, state: 'qualified', platform: staged.platform, image: staged.image,
entrypoint: '/opt/cso/entrypoint', executable: '/opt/cso/bin/scanner', version: staged.version, versionOutputSha256: HASH, helperAbi: 3,
isolationPolicyHash: staged.isolationPolicyHash, capabilities: staged.capabilities, ...(staged.assets ? { assets: staged.assets } : {}),
qualifiedAt: '2026-01-01T00:00:00.000Z', qualification: { sourceCommit: 'a'.repeat(40), workflow: 'https://github.com/example/example/actions/runs/1', sbomDigest: DIGEST, provenanceDigest: DIGEST, verifiedProvenance: true, containmentPassed: true, adapterContractPassed: true, offlineAssetsPassed: true } };
const manifest = { version: 3 as const, root: source, createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 60_000).toISOString(), entries: [], headCommit: 'a'.repeat(40), originalHash: sha256(canonical([])), executionHash: sha256(canonical([])) };
const input: ScannerRunInput = { id, runId: `scanner-qualification-${Date.now()}`, runDir: root, manifest, policy: { mode: 'comprehensive', scope: 'qualification', diff: false, base: 'HEAD', offline: true, budgetSeconds: 600, maxWorkers: 3, maxRepairs: 3 }, executionDeadline: Date.now() + 120_000, platform: staged.platform, watchdogPath: watchdog, ...(api ? { request: { api } } : {}) };
let runtime: QualifiedRuntime | undefined, application: any;
if (api) {
runtime = { id: 'qualification-runtime', stack: 'python', platform: staged.platform, state: 'qualified', image: staged.image, entrypoint: '/opt/cso/entrypoint', helperAbi: 3, versions: { python: '3.14.0', uv: '1.0.0', 'cso-preparation': '1.0.0' }, policyVersion: 'cso-isolation-v1', qualifiedAt: '2026-01-01T00:00:00.000Z', qualification: { kind: 'application', sourceCommit: 'a'.repeat(40), workflow: 'https://github.com/example/example/actions/runs/1', sbomDigest: DIGEST, provenanceDigest: DIGEST, verifiedProvenance: true, containmentPassed: true, coldStartPassed: true, positiveNegativeAssertionsPassed: true, heldOutRepairPassed: true } };
application = { sourceRoot: source, environment: { PATH: '/usr/local/bin:/usr/bin:/bin', PYTHONUNBUFFERED: '1' }, proof: { dependencyClosureHash: HASH, preparedManifestHash: HASH, sourceProjectionHash: HASH, receiptHash: HASH, executionEnvironmentHash: HASH, databaseHash: HASH }, cleanup: async () => {} };
}
const runner = await createDockerScannerRunner({ input, plan, profile, runtime, application, deadline: input.executionDeadline });
try {
const version = await runner.version();
expect(version.exitCode).toBe(0); expect(version.timedOut).not.toBe(true); expect(version.truncated).not.toBe(true);
assertScannerVersionOutput(id,staged.version,version.stdout,version.stderr);
profile.versionOutputSha256 = scannerVersionHash(version.stdout, version.stderr);
const execution = await runner.scan(), outcome = parseScannerOutput(plan, { ...execution, version: staged.version, databaseUpdatedAt: staged.assets?.advisoryDatabase?.updatedAt });
expect(outcome.status).toBe('complete'); expect(outcome.gaps).toEqual([]); expect(outcome.version).toBe(staged.version);
fs.writeFileSync(path.resolve(process.env.GSTACK_CSO_SCANNER_VERSION_HASH!), `${profile.versionOutputSha256}\n`, { flag: 'wx', mode: 0o600 });
} finally { await runner.cleanup(); }
}, 180_000);
});
+220
View File
@@ -0,0 +1,220 @@
import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import { join } from 'node:path';
import { CsoError, SnapshotManifest, canonical, sha256, snapshotPathHandle, snapshotPathId } from '../lib/cso/contracts';
import { ISOLATION_POLICY_HASH } from '../lib/cso/docker';
import { QualifiedScanner, SCANNER_CATALOG, ScannerCatalog, scannerVersionHash, selectScanner, validateScannerCatalog } from '../lib/cso/scanner-catalog';
import { ScannerRequest, ScannerRunInput, ScannerRunnerContext, executeScanner, prepareDockerScannerApplication, schemathesisControlRole, validateScannerRequest } from '../lib/cso/scanner-executor';
import { total } from '../lib/cso/admission';
import { SCANNER_IDS, ScannerExecution, ScannerId, scannerPlans } from '../lib/cso/scanners';
import { type PreparationSandboxRunner } from '../lib/cso/preparation-executor';
import { canonicalStartPlan } from '../lib/cso/verification';
import { completeRuntimeCatalogFixture } from './helpers/cso-runtime-catalog';
const roots: string[] = [];
afterEach(() => { for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); });
const HASH = 'a'.repeat(64), DIGEST = `sha256:${HASH}`;
const version = (id: ScannerId) => `${id} version ${id === 'osv' ? '2.4.0' : '4.0.0'}\n`;
function profile(id: ScannerId, platform: 'linux/amd64' | 'linux/arm64' = 'linux/amd64'): QualifiedScanner {
const arch = platform.endsWith('arm64') ? 'arm64' : 'amd64';
return { id: `${id}-test-${arch}`, scanner: id, state: 'qualified', platform, image: `ghcr.io/garrytan/gstack/cso-scanners/${id}-${arch}@${DIGEST}`,
entrypoint: '/opt/cso/entrypoint', executable: '/opt/cso/bin/scanner', version: id === 'osv' ? '2.4.0' : '4.0.0', versionOutputSha256: scannerVersionHash(version(id)), helperAbi: 3, isolationPolicyHash: ISOLATION_POLICY_HASH,
capabilities: scannerPlans({ snapshotRoot: '/source', offline: true, selected: [id] })[0].requiredFeatures,
...(id === 'semgrep' ? { assets: { semgrepRules: { path: '/policy/catalog/semgrep.yml', sha256: HASH } } } : {}),
...(['osv', 'trivy'].includes(id) ? { assets: { advisoryDatabase: { path: '/opt/cso/scanner-data/db', contentSha256: HASH, updatedAt: '2026-09-09T00:00:00.000Z', ecosystems: ['npm'] } } } : {}),
qualifiedAt: '2026-09-09T00:00:00.000Z', qualification: { sourceCommit: 'b'.repeat(40), workflow: 'https://github.com/garrytan/gstack/actions/runs/42', sbomDigest: DIGEST, provenanceDigest: DIGEST, verifiedProvenance: true, containmentPassed: true, adapterContractPassed: true, offlineAssetsPassed: true } };
}
function catalog(..._ids: ScannerId[]): ScannerCatalog {
const scanners = SCANNER_IDS.flatMap(id => [profile(id), profile(id, 'linux/arm64')]);
return { schemaVersion: 1, helperAbi: 3, revision: 'unit-fixture-only', promotion: { sourceCommit: 'b'.repeat(40), workflow: 'https://github.com/garrytan/gstack/actions/runs/42', evidenceDigest: `sha256:${sha256(canonical(scanners))}` }, scanners };
}
const scannerProfile = (c: ScannerCatalog, id: ScannerId, platform: 'linux/amd64' | 'linux/arm64' = 'linux/amd64') => c.scanners.find(item => item.scanner === id && item.platform === platform)!;
const runtimes = completeRuntimeCatalogFixture('scanner-runtime-fixture');
function request(): ScannerRequest {
return { api: { runtimeProfile: 'node', port: 3100, start: { executable: '/usr/local/bin/node', args: ['app.js'] }, control: { name: 'legitimate user', path: '/users', method: 'GET', expected: { status: 200, includes: 'users' } }, boundaryFiles: ['app.js'], operationIds: ['readUsers'], schema: { openapi: '3.1.0', info: { title: 'Unit fixture', version: '1.0.0' }, paths: { '/users': { get: { operationId: 'readUsers', responses: { '200': { description: 'Users' } } } } } } } };
}
function input(id: ScannerId): ScannerRunInput {
const dir = fs.mkdtempSync(join(fs.realpathSync(os.tmpdir()), 'cso-scanner-executor-')); roots.push(dir);
const root = join(dir, 'snapshot'); fs.mkdirSync(root, { mode: 0o700 });
const files = { 'app.js': 'export const app = true;\n', 'package.json': '{"name":"fixture","version":"1.0.0"}\n', 'package-lock.json': '{"name":"fixture","version":"1.0.0","lockfileVersion":3,"packages":{"":{"name":"fixture","version":"1.0.0"}}}\n' };
const entries = Object.entries(files).map(([path, body]) => { fs.writeFileSync(join(root, path), body, { mode: 0o600 }); return { path, pathId: snapshotPathId(root,path), originalHash: sha256(body), executionHash: sha256(body), mode: 0o600, bytes: Buffer.byteLength(body) }; }).sort((a, b) => a.path.localeCompare(b.path));
const manifest: SnapshotManifest = { version: 3, root, createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 86400000).toISOString(), entries, headCommit: 'b'.repeat(40), originalHash: sha256(canonical(entries.map(e => [e.path, e.originalHash, e.mode]))), executionHash: sha256(canonical(entries.map(e => [e.path, e.executionHash, e.mode]))) };
return { id, runId: 'unit-scanner-run', runDir: dir, manifest, policy: { mode: id === 'schemathesis' ? 'comprehensive' : 'daily', scope: 'default', diff: false, base: 'origin/main', offline: true, budgetSeconds: 600, maxWorkers: 3, maxRepairs: 3 }, executionDeadline: Date.now() + 300000, platform: 'linux/amd64', watchdogPath: '/trusted/test-watchdog', ...(id === 'schemathesis' ? { request: request() } : {}) };
}
function preparedApplication(spec: ScannerRunInput, cleanup: () => void = () => {}) {
return { sourceRoot: join(spec.runDir, 'snapshot'), environment: { PATH: '/usr/local/bin:/usr/bin:/bin' },
proof: { dependencyClosureHash: HASH, preparedManifestHash: HASH, sourceProjectionHash: HASH, receiptHash: HASH, executionEnvironmentHash: HASH, databaseHash: HASH },
cleanup: async () => { cleanup(); } };
}
function result(id: ScannerId): ScannerExecution {
const data: Record<ScannerId, unknown> = {
gitleaks: [{ RuleID: 'test-rule', Description: 'Candidate secret', File: '/source/app.js', StartLine: 1 }],
osv: { results: [{ source: { path: '/source/package-lock.json' }, packages: [{ package: { name: 'fixture-package', version: '1.0.0', ecosystem: 'npm' }, vulnerabilities: [{ id: 'CVE-2026-12345', summary: 'Candidate dependency defect' }] }] }] },
semgrep: { results: [{ check_id: 'test-rule', path: '/source/app.js', start: { line: 1, col: 1 }, extra: { message: 'Candidate code defect', severity: 'ERROR' } }], errors: [], paths: { scanned: ['/source/app.js'] } },
zizmor: { version: '2.1.0', runs: [{ tool: { driver: { name: 'zizmor', version: '4.0.0' } }, results: [{ ruleId: 'test-rule', message: { text: 'Candidate workflow defect' }, locations: [{ physicalLocation: { artifactLocation: { uri: 'app.js' }, region: { startLine: 1 } } }] }] }] },
trivy: { SchemaVersion: 2, Results: [{ Target: 'app.js', Secrets: [{ RuleID: 'test-rule', Title: 'Candidate secret', Severity: 'HIGH', StartLine: 1 }] }] },
schemathesis: { schemathesis_version: '4.0.0', complete: true, stop_reason: 'completed', operations: { selected: 1, tested: 1, errored: 0, skipped: 0 }, errors: [], failures: [{ type: 'ServerError', title: 'Candidate API failure', severity: 'high', operations: ['GET /users'] }] },
};
return { stdout: JSON.stringify(data[id]), stderr: '', exitCode: id === 'gitleaks' ? 10 : ['osv', 'schemathesis'].includes(id) ? 1 : 0 };
}
describe('reviewed scanner catalog contract', () => {
test('the shipped catalog has no executable unqualified fallback', () => { expect(SCANNER_CATALOG.scanners).toEqual([]); expect(() => selectScanner('gitleaks', 'linux/amd64')).toThrow('No qualified'); });
test.each(SCANNER_IDS)('selects a qualified immutable %s profile', id => { const c = catalog(id); validateScannerCatalog(c); expect(selectScanner(id, 'linux/amd64', undefined, c)).toEqual(scannerProfile(c, id)); });
test.each(['tag', 'entrypoint', 'policy', 'abi', 'qualification', 'executable', 'version', 'capabilities'])('rejects unreviewed %s', field => {
const c = catalog('gitleaks'), p = scannerProfile(c, 'gitleaks') as any;
if (field === 'tag') p.image = 'gitleaks:latest';
if (field === 'entrypoint') p.entrypoint = '/bin/sh';
if (field === 'policy') p.isolationPolicyHash = HASH;
if (field === 'abi') p.helperAbi = 2;
if (field === 'qualification') p.qualification.verifiedProvenance = false;
if (field === 'executable') p.executable = '/source/scanner';
if (field === 'version') p.versionOutputSha256 = 'unknown';
if (field === 'capabilities') p.capabilities = [];
expect(() => validateScannerCatalog(c)).toThrow();
});
test('missing IDs, duplicates, and platform substitution are rejected', () => {
const c = catalog('gitleaks'); delete (scannerProfile(c, 'gitleaks') as any).id; expect(() => validateScannerCatalog(c)).toThrow();
const duplicate = catalog('gitleaks'); duplicate.scanners[1].id = duplicate.scanners[0].id; expect(() => validateScannerCatalog(duplicate)).toThrow();
expect(() => selectScanner('gitleaks', 'linux/arm64', 'missing-profile', catalog('gitleaks'))).toThrow('No qualified');
});
test('rules and databases cannot point into repository source or mutable work space', () => {
const rules = catalog('semgrep'); scannerProfile(rules, 'semgrep').assets!.semgrepRules!.path = '/source/rules.yml'; expect(() => validateScannerCatalog(rules)).toThrow();
const db = catalog('osv'); scannerProfile(db, 'osv').assets!.advisoryDatabase!.path = '/work/cache'; expect(() => validateScannerCatalog(db)).toThrow();
});
test('rejects a self-asserted partial catalog even when its one profile is otherwise plausible', () => {
const c = catalog('gitleaks'); c.scanners = [scannerProfile(c, 'gitleaks')]; expect(() => validateScannerCatalog(c)).toThrow('Incompatible scanner catalog');
});
});
describe('all six helper-owned scanner execution paths', () => {
test.each(SCANNER_IDS)('%s reaches version, scan, normalization, and cleanup through an injected runner', async id => {
const calls: string[] = []; let captured: ScannerRunnerContext | undefined;
const spec = input(id), record = await executeScanner(spec, { catalog: catalog(id), runtimes,
...(id === 'schemathesis' ? { applicationPreparer: async () => preparedApplication(spec) } : {}),
runnerFactory: async context => { captured = context; return { version: async () => { calls.push('version'); return { stdout: version(id), exitCode: 0 }; }, scan: async () => { calls.push('scan'); return result(id); }, cleanup: async () => { calls.push('cleanup'); } }; } });
expect(calls).toEqual(['version', 'scan', 'cleanup']);
expect(captured!.plan.execution).toBe('sandbox');
expect(captured!.plan.network).toBe(id === 'schemathesis' ? 'loopback' : 'none');
expect(captured!.plan.args.join(' ')).not.toMatch(/download|--network|docker.sock|--metrics=on/);
expect(captured!.deadline).toBeLessThanOrEqual(Date.now() + 300000);
expect(record.outcome.status).toBe('complete'); expect(record.outcome.candidates).toHaveLength(1);
expect(record.outcome.candidates[0]).toMatchObject({ evidence: 'scanner-candidate', trust: 'untrusted' });
expect(record.outcome.candidates[0]).not.toHaveProperty('reproduced'); expect(record.outcome.candidates[0]).not.toHaveProperty('repair');
expect(record.coverage).toMatchObject({ domain: `scanner:${id}`, status: 'assessed' });
expect(record.provenance.versionOutputSha256).toBe(profile(id).versionOutputSha256);
expect(record.provenance.image).toBe(profile(id).image);
if (id === 'schemathesis') { expect(captured!.application?.sourceRoot).toBe(join(spec.runDir, 'snapshot')); expect(record.provenance.preparation?.dependencyClosureHash).toBe(HASH); }
else expect(record.provenance.preparation).toBeNull();
if (id === 'osv' || id === 'trivy') expect(record.outcome.databaseUpdatedAt).toBe('2026-09-09T00:00:00.000Z');
});
test('missing catalog never creates a runner and records the exact prerequisite', async () => {
let calls = 0; const out = await executeScanner(input('gitleaks'), { runnerFactory: async () => { calls++; throw new Error('must not run'); } });
expect(calls).toBe(0); expect(out.coverage.status).toBe('not_assessed'); expect(out.outcome.gaps[0].message).toContain('No qualified gitleaks');
});
test.each(['semgrep', 'osv', 'trivy'] as ScannerId[])('%s missing baked assets cannot launch a scanner or download replacements', async id => {
let calls = 0; const c = catalog(id); delete scannerProfile(c, id).assets;
const out = await executeScanner(input(id), { catalog: c, runnerFactory: async () => { calls++; throw new Error('must not run'); } });
expect(calls).toBe(0); expect(out.outcome.status).toBe('not_assessed'); expect(out.outcome.gaps[0].code).toBe('PREREQUISITE');
});
test('version mismatch cleans up without scanning', async () => {
const calls: string[] = [];
const out = await executeScanner(input('gitleaks'), { catalog: catalog('gitleaks'), runnerFactory: async () => ({ version: async () => ({ stdout: 'different version', exitCode: 0 }), scan: async () => { calls.push('scan'); return result('gitleaks'); }, cleanup: async () => { calls.push('cleanup'); } }) });
expect(calls).toEqual(['cleanup']); expect(out.outcome.status).toBe('not_assessed'); expect(out.outcome.version).toBeNull(); expect(out.outcome.gaps[0].message).toContain('version output');
});
test('an exact output hash cannot launder a substring version mismatch',async()=>{
const c=catalog('gitleaks'),p=scannerProfile(c,'gitleaks'),reported='gitleaks version 14.0.0\n',calls:string[]=[];
p.versionOutputSha256=scannerVersionHash(reported);c.promotion!.evidenceDigest=`sha256:${sha256(canonical(c.scanners))}`;
const out=await executeScanner(input('gitleaks'),{catalog:c,runnerFactory:async()=>({version:async()=>({stdout:reported,exitCode:0}),scan:async()=>{calls.push('scan');return result('gitleaks');},cleanup:async()=>{calls.push('cleanup');}})});
expect(calls).toEqual(['cleanup']);expect(out.outcome.status).toBe('not_assessed');expect(out.outcome.gaps[0].message).toContain('exact catalog version');
});
test.each([['timeout', 'TIMEOUT'], ['redaction', 'REDACTION_FAILED'], ['invalid-output', 'INVALID_OUTPUT'], ['tool', 'TOOL_FAILED'], ['cleanup', 'ISOLATION_FAILED']] as const)('%s preserves truthful scanner coverage', async (failure, code) => {
let cleaned = false;
const out = await executeScanner(input('gitleaks'), { catalog: catalog('gitleaks'), runnerFactory: async () => ({ version: async () => ({ stdout: version('gitleaks'), exitCode: 0 }), scan: async () => { if (failure === 'timeout') throw new CsoError('DEADLINE', 'Scanner timed out'); if (failure === 'redaction') throw new CsoError('REDACTION_FAILED', 'Scanner output withheld'); if (failure === 'tool') throw new CsoError('TOOL_FAILED', 'Scanner process failed'); return failure === 'invalid-output' ? { stdout: '{', exitCode: 0 } : result('gitleaks'); }, cleanup: async () => { cleaned = true; if (failure === 'cleanup') throw new CsoError('ISOLATION_FAILED', 'Exact cleanup failed'); } }) });
expect(cleaned).toBe(true); expect(out.outcome.status).toBe('not_assessed'); expect(out.outcome.candidates).toEqual([]); expect(out.outcome.gaps[0].code).toBe(code);
});
test('online policy still supplies only offline scanner plans', async () => {
const spec = input('osv'); spec.policy.offline = false;
await executeScanner(spec, { catalog: catalog('osv'), runnerFactory: async c => { expect(c.plan.network).toBe('none'); expect(c.plan.args).toContain('--offline'); return { version: async () => ({ stdout: version('osv'), exitCode: 0 }), scan: async () => result('osv'), cleanup: async () => {} }; } });
});
test('expired or changed retained source cannot enter the runner', async () => {
for (const expired of [true, false]) {
const spec = input('gitleaks'); if (expired) spec.manifest.expiresAt = '2000-01-01T00:00:00.000Z'; else fs.writeFileSync(join(spec.runDir, 'snapshot/app.js'), 'changed');
let calls = 0; const out = await executeScanner(spec, { catalog: catalog('gitleaks'), runnerFactory: async () => { calls++; throw new Error('must not run'); } });
expect(calls).toBe(0); expect(out.outcome.status).toBe('not_assessed');
}
});
test('source changed during collection invalidates the candidate evidence', async () => {
const spec = input('gitleaks');
const out = await executeScanner(spec, { catalog: catalog('gitleaks'), runnerFactory: async () => ({ version: async () => ({ stdout: version('gitleaks'), exitCode: 0 }), scan: async () => { fs.writeFileSync(join(spec.runDir, 'snapshot/app.js'), 'racing change'); return result('gitleaks'); }, cleanup: async () => {} }) });
expect(out.outcome.candidates).toEqual([]); expect(out.outcome.gaps[0].message).toContain('snapshot changed');
});
test('deadline is subordinate to the reporting reserve', async () => {
const spec = input('gitleaks'); spec.executionDeadline = Date.now() - 1; let calls = 0;
const out = await executeScanner(spec, { catalog: catalog('gitleaks'), runnerFactory: async () => { calls++; throw new Error('must not run'); } });
expect(calls).toBe(0); expect(out.outcome.gaps[0].code).toBe('TIMEOUT');
});
});
describe('Schemathesis application admission', () => {
test('opaque boundary and startup handles resolve only against the retained manifest',async()=>{
const spec=input('schemathesis'),raw=request(),app=spec.manifest.entries.find(entry=>entry.path==='app.js')!,handle=snapshotPathHandle(app.pathId);raw.api!.boundaryFiles=[handle];raw.api!.start.args=[handle];spec.request=raw;let captured:ScannerRunnerContext|undefined;
const record=await executeScanner(spec,{catalog:catalog('schemathesis'),runtimes,applicationPreparer:async()=>preparedApplication(spec),runnerFactory:async context=>{captured=context;return{version:async()=>({stdout:version('schemathesis'),exitCode:0}),scan:async()=>result('schemathesis'),cleanup:async()=>{}};}});
expect(record.outcome.status).toBe('complete');expect(captured!.input.request!.api).toMatchObject({boundaryFiles:['app.js'],start:{args:['app.js']}});expect(record.provenance.requestHash).toBe(sha256(canonical(validateScannerRequest(raw,'schemathesis'))));
});
test('Rails with PostgreSQL and its control verifier fits the aggregate group admission', () => {
expect(schemathesisControlRole()).toBe('verifier');
expect(total(['anchor', 'postgres', 'app', schemathesisControlRole()]).memoryMiB).toBe(3904);
expect(() => total(['anchor', 'postgres', 'app', 'tests'])).toThrow('aggregate reproduction-group limit');
});
test('daily mode blocks application execution before creating a runner', async () => {
const spec = input('schemathesis'); spec.policy.mode = 'daily'; let calls = 0;
const out = await executeScanner(spec, { catalog: catalog('schemathesis'), runtimes, runnerFactory: async () => { calls++; throw new Error('must not run'); } });
expect(calls).toBe(0); expect(out.outcome.gaps[0].message).toContain('comprehensive mode');
});
test('transformed security boundaries block API execution', async () => {
const spec = input('schemathesis'); spec.manifest.entries.find(e => e.path === 'app.js')!.transformation = 'sanitized'; let calls = 0;
const out = await executeScanner(spec, { catalog: catalog('schemathesis'), runtimes, runnerFactory: async () => { calls++; throw new Error('must not run'); } });
expect(calls).toBe(0); expect(out.outcome.gaps[0].message).toContain('boundary is missing or transformed');
});
test('no runtime profile means a prerequisite, not a scanner success', async () => {
const out = await executeScanner(input('schemathesis'), { catalog: catalog('schemathesis') });
expect(out.outcome.status).toBe('not_assessed'); expect(out.outcome.gaps[0].message).toContain('runtime is unavailable');
});
test.each(['remote-ref', 'server', 'hook', 'callback', 'operation', 'unbounded', 'source-command'])('rejects hostile or ambiguous %s harness input', kind => {
const raw = request() as any;
if (kind === 'remote-ref') raw.api.schema.components = { schemas: { User: { $ref: 'https://example.test/user.json' } } };
if (kind === 'server') raw.api.schema.servers = [{ url: 'https://example.test' }];
if (kind === 'hook') raw.api.schema['x-hooks'] = 'module.py';
if (kind === 'callback') raw.api.schema.callbacks = {};
if (kind === 'operation') raw.api.operationIds = ['not-declared'];
if (kind === 'unbounded') raw.api.maxExamples = 100000;
if (kind === 'source-command') raw.api.start = { executable: 'node', args: [] };
expect(() => validateScannerRequest(raw, 'schemathesis')).toThrow();
});
test('rejects an absolute synthetic server and startup boundaries that omit the canonical entrypoint', async () => {
for (const kind of ['synthetic', 'missing-entrypoint'] as const) {
const spec = input('schemathesis'), api = spec.request!.api!; let prepared = 0, runners = 0;
if (kind === 'synthetic') api.start = { executable: '/usr/local/bin/node', args: ['-e', "require('http').createServer((q,s)=>s.end('synthetic')).listen(3100)"] };
else api.boundaryFiles = ['package.json'];
const out = await executeScanner(spec, { catalog: catalog('schemathesis'), runtimes,
applicationPreparer: async () => { prepared++; return preparedApplication(spec); },
runnerFactory: async () => { runners++; throw new Error('must not run'); } });
expect(prepared).toBe(0); expect(runners).toBe(0); expect(out.outcome.status).toBe('not_assessed');
expect(out.outcome.gaps[0].message).toMatch(/helper-derived|canonical startup input/);
}
});
test('materialized application cleanup failure withholds otherwise complete API coverage', async () => {
const spec = input('schemathesis');
const out = await executeScanner(spec, { catalog: catalog('schemathesis'), runtimes,
applicationPreparer: async () => ({ ...preparedApplication(spec), cleanup: async () => { throw new CsoError('ISOLATION_FAILED', 'prepared source cleanup failed'); } }),
runnerFactory: async () => ({ version: async () => ({ stdout: version('schemathesis'), exitCode: 0 }), scan: async () => result('schemathesis'), cleanup: async () => {} }) });
expect(out.outcome.status).toBe('not_assessed'); expect(out.outcome.candidates).toEqual([]); expect(out.outcome.gaps[0].message).toContain('cleanup failed');
});
test('scanner preparation construction failure preserves Docker watchdog journals for detached recovery',async()=>{const spec=input('schemathesis'),snapshot=join(spec.runDir,'snapshot'),integrity=Buffer.alloc(64,7).toString('base64');spec.policy.offline=false;fs.writeFileSync(join(snapshot,'package.json'),JSON.stringify({name:'fixture',version:'1.0.0',dependencies:{cookie:'1.0.0'}}));fs.writeFileSync(join(snapshot,'package-lock.json'),JSON.stringify({name:'fixture',lockfileVersion:3,packages:{'':{name:'fixture',version:'1.0.0',dependencies:{cookie:'1.0.0'}},'node_modules/cookie':{name:'cookie',version:'1.0.0',resolved:'https://registry.npmjs.org/cookie/-/cookie-1.0.0.tgz',integrity:`sha512-${integrity}`}}}));const runtime=runtimes.runtimes.find(item=>item.stack==='node'&&item.platform==='linux/amd64')!,qualification={schemaVersion:1 as const,helperAbi:3,runnerId:'scanner-construction-fault',policyVersion:'cso-preparation-v1' as const,supportedStacks:['node','bun','python','rails'] as any,registryRestrictionQualified:true as const,dnsRebindingTestsPassed:true as const,acquisitionExcludesSource:true as const,offlineContainmentQualified:true as const,immutableArchiveMounts:true as const,resourceLimitsEnforced:true as const};let control='';const runnerFactory=(options:any):PreparationSandboxRunner=>({qualification,acquire:async()=>{control=join(options.controlRoot,'acquisition-fault');fs.mkdirSync(control,{recursive:true});fs.writeFileSync(join(control,'watchdog.ready'),'ready\n',{mode:0o600});fs.writeFileSync(join(control,'watchdog.event'),'cleanup incomplete; retrying exact journaled resources\n',{mode:0o600});fs.writeFileSync(join(control,'resources.journal'),`container:${'a'.repeat(64)}\n`,{mode:0o600});throw new CsoError('ISOLATION_FAILED','Exact preparation cleanup failed; detached watchdog remains responsible');},prepareOffline:async()=>{throw new Error('must not prepare');},disposePrepared:()=>{}});await expect(prepareDockerScannerApplication({input:spec,runtime,stack:'node',startPlan:canonicalStartPlan(snapshot,'node',3100),deadline:Date.now()+30_000,catalog:runtimes},{endpoint:{} as any,runnerFactory,cacheRoot:join(spec.runDir,'cache')})).rejects.toThrow('detached watchdog remains responsible');expect(fs.existsSync(join(control,'resources.journal'))).toBe(true);expect(fs.existsSync(join(control,'watchdog.ready'))).toBe(true);expect(fs.existsSync(control)).toBe(true);});
test('static scanners cannot accept application commands or arbitrary extra runner fields', () => {
expect(() => validateScannerRequest(request(), 'semgrep')).toThrow('Only Schemathesis');
expect(() => validateScannerRequest({ image: 'evil:latest' }, 'gitleaks')).toThrow('Unexpected');
});
});
+141
View File
@@ -0,0 +1,141 @@
import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { ISOLATION_POLICY_HASH } from '../lib/cso/docker';
import { sha256 } from '../lib/cso/contracts';
import { SCANNER_CATALOG, ScannerCatalog, scannerVersionHash } from '../lib/cso/scanner-catalog';
import { SCANNER_IDS, ScannerId, scannerPlans } from '../lib/cso/scanners';
import { scannerCatalogProposal, scannerAssetHash, validateScannerCatalogTransition } from '../scripts/cso-scanner-catalog';
import { scannerBuildMatrix } from '../scripts/cso-scanner-matrix';
import { verifiedStatementSetDigest } from '../scripts/cso-attestation-evidence';
const ROOT = path.resolve(import.meta.dir, '..'), HASH = 'a'.repeat(64), DIGEST = `sha256:${HASH}`;
const temps: string[] = [];
afterEach(() => { for (const dir of temps.splice(0)) fs.rmSync(dir, { recursive: true, force: true }); });
const image = (id: string) => `ghcr.io/garrytan/gstack/cso-scanners/${id}@sha256:${HASH}`;
const attested=(id:string,repository=`https://github.com/example/${id}`)=>({image:image(id),repository,sourceCommit:'b'.repeat(40),release:`v${id}-1.2.3`,signerWorkflow:`example/${id}/.github/workflows/release.yml`,signerDigest:'c'.repeat(40),provenanceStatementDigest:DIGEST,sbomStatementDigest:DIGEST});
function reviewedInputs() {
return { schemaVersion: 1, helperAbi: 3, state: 'reviewed', sbomGenerator: attested('sbom'), profiles: SCANNER_IDS.map(scanner => ({
scanner, version: scanner === 'gitleaks' ? '8.30.1' : '1.2.3', executable: `/usr/local/bin/${scanner === 'osv' ? 'osv-scanner' : scanner}`,
baseImages: { 'linux/amd64': attested(`${scanner}-base-amd64`, `https://github.com/example/${scanner}`), 'linux/arm64': attested(`${scanner}-base-arm64`, `https://github.com/example/${scanner}`) },
...(scanner === 'semgrep' ? { assets: { semgrepRules: { path: '/policy/catalog/semgrep.yml', sha256: HASH } } }
: ['osv', 'trivy'].includes(scanner) ? { assets: { advisoryDatabase: { path: `/opt/cso/scanner-data/${scanner}`, contentSha256: HASH, updatedAt: '2026-09-10T00:00:00.000Z', ecosystems: ['npm', 'PyPI'] } } } : {}),
...(scanner === 'schemathesis' ? { applicationExecutable: '/usr/local/bin/python' } : {}),
})) };
}
function qualified(scanner: ScannerId, platform: 'linux/amd64' | 'linux/arm64') {
const arch = platform.endsWith('arm64') ? 'arm64' : 'amd64', version = scanner === 'gitleaks' ? '8.30.1' : '1.2.3';
return { id: `${scanner}-${version}-${arch}`, scanner, state: 'qualified' as const, platform, image: image(`${scanner}-${arch}`), entrypoint: '/opt/cso/entrypoint' as const, executable: '/opt/cso/bin/scanner', version,
versionOutputSha256: scannerVersionHash(`${scanner} ${version}\n`), helperAbi: 3 as const, isolationPolicyHash: ISOLATION_POLICY_HASH,
capabilities: scannerPlans({ snapshotRoot: '/source', offline: true, selected: [scanner] })[0].requiredFeatures,
...(scanner === 'semgrep' ? { assets: { semgrepRules: { path: '/policy/catalog/semgrep.yml', sha256: HASH } } }
: ['osv', 'trivy'].includes(scanner) ? { assets: { advisoryDatabase: { path: `/opt/cso/scanner-data/${scanner}`, contentSha256: HASH, updatedAt: '2026-09-10T00:00:00.000Z', ecosystems: ['npm'] } } } : {}),
qualifiedAt: '2026-09-10T00:00:00.000Z', qualification: { sourceCommit: 'c'.repeat(40), workflow: 'https://github.com/garrytan/gstack/actions/runs/42', sbomDigest: DIGEST, provenanceDigest: DIGEST, verifiedProvenance: true as const, containmentPassed: true as const, adapterContractPassed: true as const, offlineAssetsPassed: true as const } };
}
describe('CSO scanner release inputs', () => {
test('requires all six scanners on both native architectures with derived adapter capabilities', () => {
const matrix = scannerBuildMatrix(reviewedInputs()); expect(matrix.include).toHaveLength(12);
for (const scanner of SCANNER_IDS) {
const rows = matrix.include.filter(row => row.scanner === scanner);
expect(rows.map(row => row.platform)).toEqual(['linux/amd64', 'linux/arm64']);
expect(rows.map(row => row.runner)).toEqual(['ubuntu-24.04', 'ubuntu-24.04-arm']);
expect(rows[0].capabilities).toEqual([...scannerPlans({ snapshotRoot: '/source', offline: true, selected: [scanner] })[0].requiredFeatures].sort());
}
});
test('pending, partial, mutable, assetless, or unverified input cannot publish', () => {
const pending = reviewedInputs() as any; pending.state = 'pending'; expect(() => scannerBuildMatrix(pending)).toThrow('MISSING_REVIEWED_SCANNER_INPUTS');
const partial = reviewedInputs() as any; partial.profiles.pop(); expect(() => scannerBuildMatrix(partial)).toThrow('INCOMPLETE_SCANNER_MATRIX');
const tagged = reviewedInputs() as any; tagged.profiles[0].baseImages['linux/amd64'].image = 'ghcr.io/example/gitleaks:latest'; expect(() => scannerBuildMatrix(tagged)).toThrow('INVALID_UPSTREAM_EVIDENCE');
const assetless = reviewedInputs() as any; delete assetless.profiles.find((p: any) => p.scanner === 'osv').assets; expect(() => scannerBuildMatrix(assetless)).toThrow('MISSING_OFFLINE_ASSET');
const unverified = reviewedInputs() as any; delete unverified.profiles[0].baseImages['linux/amd64'].signerDigest; expect(() => scannerBuildMatrix(unverified)).toThrow('INVALID_UPSTREAM_EVIDENCE');
const generator = reviewedInputs() as any; generator.sbomGenerator.provenanceStatementDigest = 'unreviewed'; expect(() => scannerBuildMatrix(generator)).toThrow('UNVERIFIED_SBOM_GENERATOR');
});
test('binds verified attestation statements to the reviewed subject and predicate',()=>{
const predicate='https://slsa.dev/provenance/v1',statement={_type:'https://in-toto.io/Statement/v1',subject:[{name:'image',digest:{sha256:HASH}}],predicateType:predicate,predicate:{buildType:'https://example.test/builder'}};
const value=[{attestation:{},verificationResult:{statement}}],digest=verifiedStatementSetDigest(value,predicate,HASH);
expect(digest).toMatch(/^sha256:[a-f0-9]{64}$/);expect(verifiedStatementSetDigest(value,predicate,HASH)).toBe(digest);
expect(()=>verifiedStatementSetDigest(value,'https://spdx.dev/Document/v2.3',HASH)).toThrow('VERIFIED_ATTESTATION_IDENTITY_MISMATCH');
expect(()=>verifiedStatementSetDigest(value,predicate,'d'.repeat(64))).toThrow('VERIFIED_ATTESTATION_IDENTITY_MISMATCH');
});
test('paths and matrix strings cannot inject build commands', () => {
for (const executable of ['/usr/bin/scanner;id', '../../scanner', '/usr/bin/scan ner', '/usr/bin/scanner\nBAD=1']) {
const input = reviewedInputs() as any; input.profiles[0].executable = executable; expect(() => scannerBuildMatrix(input)).toThrow('INVALID_SCANNER_EXECUTABLE');
}
const extra = reviewedInputs() as any; extra.profiles[0].buildArgs = ['EVIL=1']; expect(() => scannerBuildMatrix(extra)).toThrow('INVALID_SCANNER_PROFILE');
});
});
describe('CSO scanner catalog promotion', () => {
const expected = { sourceCommit: 'c'.repeat(40), workflow: 'https://github.com/garrytan/gstack/actions/runs/42', imagePrefix: 'ghcr.io/garrytan/gstack/cso-scanners/' };
const fragments = () => SCANNER_IDS.flatMap(scanner => ['linux/amd64', 'linux/arm64'].map(platform => qualified(scanner, platform as any)));
test('assembles only a complete qualified matrix and records rollback identity', () => {
const proposal = scannerCatalogProposal(SCANNER_CATALOG, fragments(), 'cso-scanners-qualified-42', expected);
expect(proposal.scanners).toHaveLength(12); expect(proposal.previousRevision).toBe(SCANNER_CATALOG.revision);
expect(proposal.promotion).toMatchObject({ sourceCommit: expected.sourceCommit, workflow: expected.workflow });
expect(proposal.promotion!.evidenceDigest).toMatch(/^sha256:[a-f0-9]{64}$/);
expect(proposal.scanners.map(profile => `${profile.scanner}:${profile.platform}`)).toEqual([...proposal.scanners.map(profile => `${profile.scanner}:${profile.platform}`)].sort());
});
test('rejects missing platforms, claimed capabilities, provenance substitution, and foreign image repositories', () => {
const missing = fragments(); missing.pop(); expect(() => scannerCatalogProposal(SCANNER_CATALOG, missing, 'missing-profile', expected)).toThrow('INCOMPLETE_SCANNER_CATALOG_MATRIX');
const capabilities = fragments() as any[]; capabilities[0].capabilities = ['invented']; expect(() => scannerCatalogProposal(SCANNER_CATALOG, capabilities, 'bad-capability', expected)).toThrow('capabilities do not match');
const source = fragments() as any[]; source[0].qualification.sourceCommit = 'd'.repeat(40); expect(() => scannerCatalogProposal(SCANNER_CATALOG, source, 'wrong-source', expected)).toThrow('SOURCE_COMMIT_MISMATCH');
const foreign = fragments() as any[]; foreign[0].image = image('foreign').replace('garrytan/gstack', 'other/repo'); expect(() => scannerCatalogProposal(SCANNER_CATALOG, foreign, 'foreign-image', expected)).toThrow('Scanner profile is not qualified');
});
test('promotion is a compare-and-swap against the currently reviewed revision', () => {
const proposal = scannerCatalogProposal(SCANNER_CATALOG, fragments(), 'cso-scanners-qualified-43', expected);
expect(() => validateScannerCatalogTransition(SCANNER_CATALOG, proposal)).not.toThrow();
expect(() => validateScannerCatalogTransition({ ...SCANNER_CATALOG, revision: 'catalog-advanced-concurrently' }, proposal)).toThrow('SCANNER_CATALOG_BASE_REVISION_MISMATCH');
});
test('hashes files and trees deterministically and rejects linked asset payloads', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cso-scanner-assets-')); temps.push(root); fs.mkdirSync(path.join(root, 'nested')); fs.writeFileSync(path.join(root, 'nested', 'b'), 'two'); fs.writeFileSync(path.join(root, 'a'), 'one');
const first = scannerAssetHash(root); expect(first).toMatch(/^[a-f0-9]{64}$/); expect(scannerAssetHash(root)).toBe(first); expect(scannerAssetHash(path.join(root, 'a'))).toBe(sha256('one'));
fs.symlinkSync(path.join(root, 'a'), path.join(root, 'linked')); expect(() => scannerAssetHash(root)).toThrow('UNSAFE_SCANNER_ASSET');
const top = path.join(path.dirname(root), `${path.basename(root)}-link`); temps.push(top); fs.symlinkSync(root, top); expect(() => scannerAssetHash(top)).toThrow('UNSAFE_SCANNER_ASSET');
});
});
describe('CSO scanner qualification workflow', () => {
test('keeps branch validation read-only while qualification and promotion are protected-main and review gated', () => {
const raw = fs.readFileSync(path.join(ROOT, '.github/workflows/cso-scanner-images.yml'), 'utf8'), workflow = Bun.YAML.parse(raw) as any;
expect(Object.keys(workflow.on)).toEqual(['workflow_dispatch']); expect(workflow.permissions).toEqual({ contents: 'read' }); expect(workflow.jobs['reviewed-inputs'].if).toBeUndefined();
const stage = workflow.jobs['stage-and-qualify'];
expect(stage.if).toContain("github.ref == 'refs/heads/main'"); expect(stage.if).toContain("github.event_name == 'workflow_dispatch'"); expect(stage.environment).toBe('cso-scanner-release');
expect(stage.permissions).toMatchObject({ contents: 'read', packages: 'write', 'id-token': 'write', attestations: 'write', 'artifact-metadata': 'write' });
const privileged = Object.entries(workflow.jobs).filter(([, job]: any) => ['packages', 'id-token', 'attestations', 'artifact-metadata'].some(permission => job.permissions?.[permission] === 'write'));
expect(privileged.map(([name]) => name)).toEqual(['stage-and-qualify']);
for (const permission of ['packages', 'id-token', 'attestations', 'artifact-metadata']) expect(workflow.jobs['reviewed-inputs'].permissions?.[permission]).toBeUndefined();
expect(workflow.jobs['promote-catalog'].if).toContain("github.ref == 'refs/heads/main'"); expect(workflow.jobs['promote-catalog'].if).toContain('inputs.promote_catalog == true'); expect(workflow.jobs['promote-catalog'].environment).toBe('cso-scanner-release');
expect(workflow.jobs['promote-catalog'].permissions.packages).toBe('read');
expect(raw).toContain('test/cso-scanners.test.ts test/cso-scanner-executor.test.ts test/cso-scanner-release.test.ts');
expect(raw).toContain('test/cso-scanner-docker-integration.test.ts'); expect(raw).toContain('GSTACK_CSO_SCANNER_VERSION_HASH'); expect(raw).toContain('hash-asset scanner-asset');
expect(raw).toContain('Require a public package and anonymously load the verified immutable image');
expect(raw).toContain('scripts/cso-public-ghcr.ts verify');
expect(raw).toContain('--repository "$GITHUB_REPOSITORY" --output public-image.json');
expect(raw).toContain('sha256sum "$output" staged-profile.json version.sha256 declared-assets.json public-image.json');
const anonymousStage = workflow.jobs['stage-and-qualify'].steps.find((step: any) => step.run?.includes('scripts/cso-public-ghcr.ts verify'));
expect(anonymousStage.env.GH_TOKEN).toBe('${{ github.token }}');
for(const value of ['--signer-workflow "$signer_workflow"','--signer-digest "$signer_digest"','--source-digest "$source_commit"','cso-attestation-evidence.ts digest','provenanceStatementDigest','sbomStatementDigest'])expect(raw).toContain(value);
expect(raw).not.toContain('cso-scanner-staging');
const docker = fs.readFileSync(path.join(ROOT, 'lib/cso/docker.ts'), 'utf8');
for (const flag of ["'--pull=never'", "'--read-only'", "'--cap-drop','ALL'", "'no-new-privileges:true'", "'seccomp=builtin'", "'--log-driver=none'", "'--network'"]) expect(docker).toContain(flag);
expect(docker).toContain("['rm','--force','--volumes',id]"); expect(docker).toContain('Pinned runtime image declares writable volumes');
expect(raw).toContain('cso-scanner-catalog.ts assemble'); expect(raw).toContain('cso-scanner-catalog.ts validate-transition lib/cso/scanner-images/catalog.json promotion/catalog-proposal.json'); expect(raw).toContain('gh pr create --base main');
expect(raw).toContain('branch="cso-scanner-catalog-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"'); expect(raw).not.toContain('branch="cso-scanner-catalog-$GITHUB_RUN_ID"');
const publicPromotion = raw.indexOf('Recheck public visibility and anonymous pulls before promotion');
const sourcePromotion = raw.indexOf('Revalidate and open the reviewable source catalog PR');
expect(publicPromotion).toBeGreaterThanOrEqual(0); expect(sourcePromotion).toBeGreaterThan(publicPromotion);
expect(raw.slice(publicPromotion, sourcePromotion)).toContain('--remove-after');
for (const job of Object.values(workflow.jobs) as any[]) for (const step of job.steps) if (step.uses) expect(step.uses).toMatch(/@[a-f0-9]{40}$/);
});
test('commits no invented release input and normalizes scanner images to the constrained helper contract', () => {
const inputs = JSON.parse(fs.readFileSync(path.join(ROOT, 'lib/cso/scanner-images/build-inputs.json'), 'utf8'));
expect(inputs).toMatchObject({ state: 'pending', profiles: [], sbomGenerator: null });
const dockerfile = fs.readFileSync(path.join(ROOT, 'lib/cso/scanner-images/scanner.Dockerfile'), 'utf8');
for (const value of ['/opt/cso/entrypoint', '/opt/cso/bin/scanner', 'USER 10001:10001', 'test -x /bin/sleep', 'test -x /bin/cat']) expect(dockerfile).toContain(value);
expect(fs.readFileSync(path.join(ROOT, 'lib/cso/scanner-executor.ts'), 'utf8')).toContain("capture(['/bin/cat', plan.outputPath])");
const postgres = fs.readFileSync(path.join(ROOT, 'lib/cso/images/postgresql.Dockerfile'), 'utf8');
for (const value of ['FROM ${BASE_IMAGE} AS upstream', 'FROM scratch', 'COPY --from=upstream / /']) expect(postgres).toContain(value);
});
});
+337
View File
@@ -0,0 +1,337 @@
import { describe, expect, test } from 'bun:test';
import {
importSarif, MAX_SCANNER_OUTPUT_BYTES, parseScannerOutput, scannerLocation,
scannerPlans, validateScannerBaseUrl, type ScannerId, type ScannerPlan,
} from '../lib/cso/scanners';
function plan(id: ScannerId): ScannerPlan {
return scannerPlans({
snapshotRoot: '/src', offline: true, selected: [id], semgrepRules: '/policy/rules.yml', advisoryCache: '/advisories',
schemaPath: '/policy/openapi.json', baseUrl: 'http://127.0.0.1:3000/', operationIds: ['readDocument'],
})[0];
}
function parse(id: ScannerId, data: unknown, exitCode = 0) {
return parseScannerOutput(plan(id), { stdout: JSON.stringify(data), exitCode, version: 'scanner 2.3.0', databaseUpdatedAt: '2026-09-09T00:00:00.000Z' });
}
function sarif(result?: Record<string, unknown>) {
return { version: '2.1.0', runs: [{ tool: { driver: { name: 'CodeQL', version: '2.22.0', rules: [{ id: 'js/sql-injection', defaultConfiguration: { level: 'error' } }] } }, results: result ? [result] : [] }] };
}
function sarifResult(uri = 'src/routes.ts') {
return { ruleIndex: 0, message: { text: 'Untrusted input reaches a query.' }, locations: [{ physicalLocation: { artifactLocation: { uri }, region: { startLine: 12, startColumn: 3 } } }] };
}
describe('CSO scanner execution plans', () => {
test('all six plans require containment and never acquire dependencies or inherit credentials', () => {
const plans = scannerPlans({ snapshotRoot: '/src', offline: false });
expect(plans.map(p => p.id)).toEqual(['gitleaks', 'osv', 'semgrep', 'zizmor', 'trivy', 'schemathesis']);
for (const p of plans) {
expect(p.execution).toBe('sandbox');
expect(p.network).toBe(p.id === 'schemathesis' ? 'loopback' : 'none');
expect(p.timeoutSeconds).toBeLessThanOrEqual(300);
expect(p.maxOutputBytes).toBe(MAX_SCANNER_OUTPUT_BYTES);
expect(Object.keys(p.env)).not.toContain('GITHUB_TOKEN');
expect(Object.keys(p.env)).not.toContain('PATH');
expect(p.args.join(' ')).not.toMatch(/npx|uvx|--allow-local-builds|--call-analysis=|--autofix|download-offline/);
expect(p.provenanceSources.length).toBeGreaterThan(0);
}
});
test('OSV uses full offline v2 scanning with code analysis disabled', () => {
const p = plan('osv');
expect(p.args).toContain('--offline');
expect(p.args).not.toContain('--offline-vulnerabilities');
expect(p.args).toContain('--no-call-analysis=all');
expect(p.args.slice(0, 2)).toEqual(['scan', 'source']);
const old = scannerPlans({ snapshotRoot: '/src', offline: true, selected: ['osv'], advisoryCache: '/advisories', tools: { osv: { available: true, version: '1.9.0' } } })[0];
expect(old.prerequisites.join(' ')).toContain('major version 2');
});
test('Gitleaks scans current files and bypasses project suppressions with full redaction', () => {
const p = plan('gitleaks');
expect(p.args[0]).toBe('dir');
expect(p.args).toContain('--redact=100');
expect(p.args).toContain('--report-path=-');
expect(p.args).toContain('--ignore-gitleaks-allow');
expect(p.trustedFiles.some(f => f.content.includes('useDefault = true'))).toBe(true);
const history = scannerPlans({ snapshotRoot: '/src', offline: true, selected: ['gitleaks'], gitHistory: '/history' })[0];
expect(history.args[0]).toBe('git');
expect(history.prerequisites.join(' ')).toContain('no hooks');
});
test('Semgrep cannot select hosted rules, upload findings, or enable code builds', () => {
const p = plan('semgrep');
expect(p.args[0]).toBe('scan');
expect(p.args).toContain('--metrics=off');
expect(p.args).toContain('--disable-version-check');
expect(p.args).toContain('--oss-only');
expect(() => scannerPlans({ snapshotRoot: '/src', offline: true, semgrepRules: 'p/security-audit' })).toThrow();
expect(() => scannerPlans({ snapshotRoot: '/src', offline: true, semgrepRules: '/src/rules.yml' })).toThrow();
});
test('zizmor ignores environment online defaults and repo config', () => {
const p = plan('zizmor');
expect(p.args).toContain('--offline');
expect(p.args).toContain('--no-config');
expect(p.args).toContain('--no-ignores');
expect(p.env.ZIZMOR_OFFLINE).toBe('1');
});
test('Trivy suppresses telemetry, metadata calls, version checks, and every database update', () => {
const p = plan('trivy');
for (const flag of ['--disable-telemetry', '--offline-scan', '--skip-db-update', '--skip-java-db-update', '--skip-check-update', '--skip-version-check', '--skip-vex-repo-update']) expect(p.args).toContain(flag);
expect(p.args).toContain('/policy/trivy.yaml');
expect(p.args).toContain('/policy/trivyignore');
});
test('Schemathesis restricts operation count, redirects, seed, time, and report location', () => {
const p = plan('schemathesis');
expect(p.prerequisites).toEqual([]);
for (const arg of ['--workers=1', '--phases=fuzzing', '--max-redirects=0', '--seed', '--max-examples', '--max-time', '--report-json-path', '--include-operation-id']) expect(p.args).toContain(arg);
expect(p.outputPath).toBe('/work/schemathesis.json');
expect(p.coverage.scope).toEqual(['operation:readDocument']);
expect(() => scannerPlans({ snapshotRoot: '/src', offline: true, schemaPath: 'https://example.test/api.json' })).toThrow();
});
test.each(['http://example.com', 'http://localhost:3000', 'http://169.254.169.254/', 'http://127.1/', 'http://2130706433/', 'http://0x7f000001/', 'http://127.0.0.1.evil.test/', ['http://user:', 'pass@127.0.0.1/'].join(''), 'file:///tmp/app', 'http://[::ffff:127.0.0.1]/', 'http://127.0.0.1/?x=secret'])('rejects non-canonical or credential-bearing target %s', url => {
expect(() => validateScannerBaseUrl(url)).toThrow();
});
test('numeric IPv4 and IPv6 loopback are accepted', () => {
expect(validateScannerBaseUrl('http://127.0.0.1:8080/api')).toBe('http://127.0.0.1:8080/api');
expect(validateScannerBaseUrl('http://[::1]:8080/api')).toBe('http://[::1]:8080/api');
});
test('invalid bounds and source-controlled policy are rejected before execution', () => {
expect(() => scannerPlans({ snapshotRoot: '/src', offline: true, deadlineSeconds: 301 })).toThrow();
expect(() => scannerPlans({ snapshotRoot: '/src', offline: true, policyRoot: '/src/policy' })).toThrow();
expect(() => scannerPlans({ snapshotRoot: '/src', offline: true, advisoryCache: '/src/cache' })).toThrow();
expect(() => scannerPlans({ snapshotRoot: '/src/../etc', offline: true })).toThrow();
expect(() => scannerPlans({ snapshotRoot: '/src', offline: true, selected: ['osv', 'osv'] })).toThrow();
});
});
describe('CSO scanner candidate normalization', () => {
test('Gitleaks retains location and rule while discarding secret-bearing fields', () => {
const secret = 'ghp_' + 'A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8';
const result = parse('gitleaks', [{ RuleID: 'github-pat', Description: 'Possible token', File: '/src/config.ts', StartLine: 2, StartColumn: 5, Secret: secret, Match: secret, Line: secret, Author: 'person@example.test' }], 10);
expect(result.status).toBe('complete');
expect(result.candidates[0].location?.path).toBe('config.ts');
expect(result.candidates[0].evidence).toBe('scanner-candidate');
expect(result.candidates[0].trust).toBe('untrusted');
expect(JSON.stringify(result)).not.toContain(secret);
expect(JSON.stringify(result)).not.toContain('person@example.test');
});
test('OSV aliases and affected version never imply reachability or production exposure', () => {
const result = parse('osv', { results: [{ source: { path: '/src/package-lock.json' }, packages: [{ package: { name: 'vulnerable-package', version: '1.0.0', ecosystem: 'npm' }, vulnerabilities: [{ id: 'GHSA-abcd-efgh-ijkl', aliases: ['CVE-2026-12345'], summary: 'Unsafe parsing' }] }] }] }, 1);
expect(result.status).toBe('complete');
expect(result.candidates[0].dependency).toEqual({ name: 'vulnerable-package', version: '1.0.0', ecosystem: 'npm', reachability: 'unknown', exposure: 'unknown' });
expect(result.candidates[0].advisoryIds).toContain('CVE-2026-12345');
});
test('Semgrep keeps independent errors even with useful candidates', () => {
const result = parse('semgrep', { results: [{ check_id: 'sql-injection', path: 'api.ts', start: { line: 7, col: 3 }, extra: { message: 'Query input', severity: 'ERROR' } }], errors: [{ type: 'ParseError' }], paths: { scanned: ['api.ts'], skipped: ['legacy.rb'] } });
expect(result.status).toBe('partial');
expect(result.candidates).toHaveLength(1);
expect(result.gaps.map(g => g.code)).toEqual(['TOOL_FAILED', 'SKIPPED_INPUT']);
});
test('zizmor SARIF becomes candidate evidence and retains suppression', () => {
const data = sarif({ ...sarifResult('.github/workflows/deploy.yml'), ruleId: 'dangerous-triggers', suppressions: [{ kind: 'inSource' }] });
const result = parse('zizmor', data);
expect(result.status).toBe('complete');
expect(result.candidates[0].suppressed).toBe(true);
expect(result.candidates[0].tool).toBe('zizmor');
});
test('Trivy normalizes dependency, secret, and infrastructure candidates separately', () => {
const result = parse('trivy', { SchemaVersion: 2, Results: [
{ Target: 'package-lock.json', Type: 'npm', Vulnerabilities: [{ VulnerabilityID: 'CVE-2026-12345', PkgName: 'lib', InstalledVersion: '2.0.0', Severity: 'HIGH' }] },
{ Target: 'Dockerfile', Misconfigurations: [{ ID: 'DS002', Title: 'Root user', Severity: 'MEDIUM', CauseMetadata: { StartLine: 2 } }] },
{ Target: 'config.rb', Secrets: [{ RuleID: 'generic-api-key', Title: 'Key', Severity: 'CRITICAL', StartLine: 1 }] },
] });
expect(result.status).toBe('complete');
expect(result.candidates).toHaveLength(3);
expect(result.candidates[0].dependency?.reachability).toBe('unknown');
expect(result.candidates[1].location?.line).toBe(2);
});
test('Schemathesis assertion failures do not become reproduced vulnerabilities', () => {
const result = parse('schemathesis', { schemathesis_version: '4.0.0', complete: true, stop_reason: 'completed', operations: { selected: 1, tested: 1, errored: 0, skipped: 0 }, errors: [], failures: [{ type: 'ServerError', title: 'Server error', severity: 'critical', operations: ['GET /documents'] }] }, 1);
expect(result.status).toBe('complete');
expect(result.candidates[0].operation).toBe('GET /documents');
expect(result.candidates[0].evidence).toBe('scanner-candidate');
expect(result.candidates[0]).not.toHaveProperty('reproduced');
});
test('startup failure and zero exercised operations remain not covered', () => {
const result = parse('schemathesis', { schemathesis_version: '4.0.0', complete: false, stop_reason: 'interrupted', operations: null, errors: [{ title: 'Schema load failed' }], failures: [] }, 1);
expect(result.status).toBe('partial');
expect(result.candidates).toHaveLength(0);
expect(result.gaps.some(g => g.message.includes('no operations'))).toBe(true);
});
test('fingerprints do not depend on generated descriptions', () => {
const row = { RuleID: 'hardcoded-token', Description: 'Original title', File: 'config.ts', StartLine: 4 };
const a = parse('gitleaks', [row], 10), b = parse('gitleaks', [{ ...row, Description: 'New generated wording' }], 10);
expect(a.candidates[0].id).toBe(b.candidates[0].id);
expect(a.planSha256).toBe(b.planSha256);
});
});
describe('CSO scanner failure and hostile-input handling', () => {
test('missing optional scanners do not return a clean assessment', () => {
const result = parseScannerOutput(plan('gitleaks'), { stdout: '', exitCode: null, unavailable: true });
expect(result.status).toBe('not_assessed');
expect(result.gaps[0].code).toBe('UNAVAILABLE');
});
test('missing local rules/databases become precise prerequisites without downloads', () => {
const p = scannerPlans({ snapshotRoot: '/src', offline: true, selected: ['osv'] })[0];
const result = parseScannerOutput(p, { stdout: '{"results":[]}', exitCode: 0 });
expect(result.status).toBe('not_assessed');
expect(result.gaps[0].code).toBe('PREREQUISITE');
expect(result.gaps[0].message).toContain('offline OSV');
});
test('an OSV extraction failure on stderr cannot produce empty-clean JSON coverage', () => {
const result = parseScannerOutput(plan('osv'), { stdout: '{"results":[]}', stderr: 'Error during extraction: no offline version of the OSV database is available', exitCode: 0, databaseUpdatedAt: '2026-09-09T00:00:00.000Z' });
expect(result.status).toBe('partial');
expect(result.gaps.some(g => g.code === 'TOOL_FAILED')).toBe(true);
});
test.each(['', '{', 'null', '[]', '{"results":[]}'])('malformed Semgrep output %s never passes', raw => {
const result = parseScannerOutput(plan('semgrep'), { stdout: raw, exitCode: 0 });
expect(result.status).toBe('not_assessed');
expect(result.gaps[0].code).toBe('INVALID_OUTPUT');
});
test('unrecognized exit code and timeout remain gaps even with syntactically complete output', () => {
const result = parseScannerOutput(plan('gitleaks'), { stdout: '[]', exitCode: 2, timedOut: true });
expect(result.status).toBe('partial');
expect(result.gaps.map(g => g.code)).toEqual(['TIMEOUT', 'TOOL_FAILED']);
});
test('truncation withholds the entire payload including a secret split across capture chunks', () => {
const chunks = ['[{"RuleID":"x","Description":"gh', 'p_' + 'A'.repeat(36) + '","File":"x"}]'];
const result = parseScannerOutput(plan('gitleaks'), { stdout: chunks.join(''), exitCode: 10, truncated: true });
expect(result.status).toBe('not_assessed');
expect(result.gaps[0].code).toBe('OUTPUT_LIMIT');
expect(result.candidates).toHaveLength(0);
});
test('capture budget applies to UTF-8 bytes and stderr together', () => {
const result = parseScannerOutput(plan('gitleaks'), { stdout: '[]', stderr: 'λ'.repeat(MAX_SCANNER_OUTPUT_BYTES / 2), exitCode: 0 });
expect(result.gaps[0].code).toBe('OUTPUT_LIMIT');
});
test('marker-only private keys withhold the entire decoded document', () => {
const data = [{ RuleID: 'key', Description: ['-----BEGIN ', 'PRIVATE KEY-----\nsecretbody\n-----END ', 'PRIVATE KEY-----'].join(''), File: 'config' }];
const result = parse('gitleaks', data, 10);
expect(result.status).toBe('not_assessed');
expect(result.gaps[0].code).toBe('REDACTION_FAILED');
expect(JSON.stringify(result)).not.toContain('secretbody');
});
test('JSON unicode escapes cannot bypass the decoded-string redactor', () => {
const secret = 'ghp_' + 'A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8';
const raw = JSON.stringify([{ RuleID: 'key', Description: secret, File: 'config' }]).replace('ghp_', '\\u0067hp_');
const result = parseScannerOutput(plan('gitleaks'), { stdout: raw, exitCode: 10 });
expect(result.status).toBe('complete');
expect(JSON.stringify(result)).not.toContain(secret);
expect(result.candidates[0].message).toContain('REDACTED');
});
test('prototype keys and extreme nesting are rejected without leaking raw content', () => {
for (const raw of ['{"__proto__":{"polluted":true}}', '['.repeat(70) + '[]' + ']'.repeat(70)]) {
const result = parseScannerOutput(plan('gitleaks'), { stdout: raw, exitCode: 0 });
expect(result.status).toBe('not_assessed');
expect(result.gaps[0].code).toBe('INVALID_OUTPUT');
}
expect(({} as Record<string, unknown>).polluted).toBeUndefined();
});
test('unknown database freshness is explicit even when there are no advisories', () => {
const result = parseScannerOutput(plan('osv'), { stdout: '{"results":[]}', exitCode: 0 });
expect(result.status).toBe('partial');
expect(result.databaseUpdatedAt).toBeNull();
expect(result.gaps[0].code).toBe('UNKNOWN_FRESHNESS');
});
test('finding exit codes cannot certify an empty-clean report', () => {
const result = parse('gitleaks', [], 10);
expect(result.status).toBe('partial');
expect(result.gaps[0].code).toBe('INVALID_OUTPUT');
});
test('a schema version alone is not evidence of a completed Trivy scan', () => {
const result = parse('trivy', { SchemaVersion: 2 });
expect(result.status).toBe('not_assessed');
expect(result.gaps[0].code).toBe('INVALID_OUTPUT');
});
});
describe('CSO SARIF import boundary', () => {
test('CodeQL SARIF imports read-only candidates with artifact-index locations', () => {
const data = sarif(sarifResult());
const run = data.runs[0] as Record<string, any>;
run.artifacts = [{ location: { uri: 'src/routes.ts' } }];
run.results[0].locations[0].physicalLocation.artifactLocation = { index: 0 };
const result = importSarif(JSON.stringify(data), { sourceRoot: '/src' });
expect(result.status).toBe('complete');
expect(result.tool).toBe('sarif');
expect(result.candidates[0].tool).toBe('sarif');
expect(result.candidates[0].ruleId).toBe('js/sql-injection');
expect(result.candidates[0].location).toEqual({ path: 'src/routes.ts', line: 12, column: 3 });
});
test.each(['../.ssh/id_rsa', '%2e%2e/.ssh/id_rsa', '%252e%252e/.ssh/id_rsa', 'file:///etc/passwd', 'file://remote/src/file.ts', 'https://evil.test/collect', 'javascript:alert(1)', '//evil.test/file', 'C:\\Users\\secret', 'src/../../secret', 'src/%00file'])('rejects unsafe artifact URI %s', uri => {
const result = importSarif(JSON.stringify(sarif(sarifResult(uri))), { sourceRoot: '/src' });
expect(result.status).toBe('partial');
expect(result.candidates).toHaveLength(0);
expect(result.gaps.length).toBeGreaterThan(0);
expect(() => scannerLocation(uri, '/src')).toThrow();
});
test('failed invocation does not erase independently useful candidates or imply complete coverage', () => {
const data = sarif(sarifResult());
(data.runs[0] as Record<string, unknown>).invocations = [{ executionSuccessful: false }];
const result = importSarif(JSON.stringify(data), { sourceRoot: '/src' });
expect(result.status).toBe('partial');
expect(result.candidates).toHaveLength(1);
expect(result.gaps[0].code).toBe('TOOL_FAILED');
});
test('SARIF passing checks are not vulnerability candidates', () => {
const result = importSarif(JSON.stringify(sarif({ ...sarifResult(), kind: 'pass' })), { sourceRoot: '/src' });
expect(result.status).toBe('complete');
expect(result.candidates).toHaveLength(0);
});
test('an untrusted originalUriBaseIds map cannot change the assessment root', () => {
const data = sarif(sarifResult('secret.ts'));
const run = data.runs[0] as Record<string, any>;
run.originalUriBaseIds = { ROOT: { uri: 'file:///etc/' } };
run.results[0].locations[0].physicalLocation.artifactLocation.uriBaseId = 'ROOT';
const result = importSarif(JSON.stringify(data), { sourceRoot: '/src' });
expect(result.candidates).toHaveLength(0);
expect(result.status).toBe('partial');
});
test('SARIF external properties are not fetched and missing runs are not a clean scan', () => {
const result = importSarif(JSON.stringify({ version: '2.1.0', runs: [], externalProperties: [{ uri: 'https://evil.test/' }] }), { sourceRoot: '/src' });
expect(result.status).toBe('partial');
expect(result.candidates).toHaveLength(0);
expect(result.gaps[0].code).toBe('SKIPPED_INPUT');
});
test('external SARIF result references are an explicit coverage gap', () => {
const data = sarif();
(data.runs[0] as Record<string, unknown>).externalPropertyFileReferences = { results: [{ location: { uri: 'https://evil.test/results.json' } }] };
const result = importSarif(JSON.stringify(data), { sourceRoot: '/src' });
expect(result.status).toBe('partial');
expect(result.gaps[0].message).toContain('External SARIF');
});
});
@@ -0,0 +1,84 @@
import { afterEach, describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { capture } from '../lib/cso/snapshot';
const roots:string[]=[];
afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
function trustedGit():string{
const executable=Bun.which('git');
if(!executable)throw new Error('git is required for the snapshot disappearance fixture');
return executable;
}
function gitEnvironment(home:string):NodeJS.ProcessEnv{
return{...process.env,HOME:home,GIT_CONFIG_NOSYSTEM:'1',GIT_CONFIG_GLOBAL:process.platform==='win32'?'NUL':'/dev/null',GIT_TERMINAL_PROMPT:'0'};
}
type FileIdentity={path:string;dev:bigint;ino:bigint};
function canonicalFile(file:string):string{
const resolved=fs.realpathSync.native(file);
return process.platform==='win32'?resolved.replace(/^\\\\\?\\UNC\\/i,'\\\\').replace(/^\\\\\?\\/,'').toLowerCase():resolved;
}
function fileIdentity(file:string):FileIdentity{const stat=fs.statSync(file,{bigint:true});return{path:canonicalFile(file),dev:stat.dev,ino:stat.ino};}
function isFileIdentity(candidate:unknown,expected:FileIdentity):boolean{
if(path.basename(String(candidate)).toLowerCase()!=='tracked.txt')return false;
try{const file=String(candidate),current=fs.statSync(file,{bigint:true});return canonicalFile(file)===expected.path&&current.dev===expected.dev&&current.ino===expected.ino;}catch{return false;}
}
function fixture(){
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-snapshot-disappearance-'));roots.push(root);
const repo=path.join(root,'repo'),runDir=path.join(root,'run');
fs.mkdirSync(repo);fs.mkdirSync(runDir,{mode:0o700});
const git=(...args:string[])=>{const result=spawnSync(trustedGit(),['-C',repo,...args],{encoding:'utf8',env:gitEnvironment(root),timeout:30_000});if(result.status)throw new Error(result.stderr);return result.stdout.trim();};
git('init','-q');git('config','user.email','fixture@example.test');git('config','user.name','Fixture');
const tracked=path.join(repo,'tracked.txt');fs.writeFileSync(tracked,'security-relevant source\n');git('add','tracked.txt');git('commit','-qm','base');
// Match the file itself instead of one pathname spelling. Windows may hand
// capture() an 8.3, long, or \\?\-namespaced spelling for this same inode.
return{repo,runDir,tracked,trackedIdentity:fileIdentity(tracked),parked:path.join(repo,'.tracked.txt.parked')};
}
describe('CSO snapshot source-disappearance races',()=>{
// Bun on Windows does not patch the node:fs binding imported by snapshot.ts,
// so these syscall-choreography fixtures cannot activate there. The native
// Windows launcher suite instead mutates real source after capture history is
// recorded and requires start to fail closed without a snapshot or report.
test.skipIf(process.platform==='win32')('rejects a tracked path that disappears at its capture lstat and is restored before final membership validation',async()=>{
const {repo,runDir,tracked,trackedIdentity,parked}=fixture(),lstat=fs.lstatSync;
let trackedLstats=0,injected=false,failure:unknown;
const patched=spyOn(fs,'lstatSync').mockImplementation(((candidate:any,options?:any)=>{
if(isFileIdentity(candidate,trackedIdentity)&&++trackedLstats===2){
// The first lstat is rejectSpecialFiles' directory walk. The second is
// capture's per-entry existence check. Move the tracked file for that
// exact syscall, then restore it before any final Git membership check.
injected=true;fs.renameSync(tracked,parked);
try{return options===undefined?lstat(candidate):lstat(candidate,options);}
finally{fs.renameSync(parked,tracked);}
}
return options===undefined?lstat(candidate):lstat(candidate,options);
}) as typeof fs.lstatSync);
try{await capture(repo,runDir);}catch(error){failure=error;}finally{patched.mockRestore();}
expect(injected).toBe(true);
expect(fs.readFileSync(tracked,'utf8')).toBe('security-relevant source\n');
expect(failure).toMatchObject({code:'SNAPSHOT_RACE'});
});
test.skipIf(process.platform==='win32')('rejects a nonignored source file introduced during the final content validation',async()=>{
const {repo,runDir,tracked,trackedIdentity}=fixture(),late=path.join(path.dirname(tracked),'late-vulnerable.js'),lstat=fs.lstatSync;let injected=false,failure:unknown;
const patched=spyOn(fs,'lstatSync').mockImplementation(((candidate:any,options?:any)=>{
if(!injected&&isFileIdentity(candidate,trackedIdentity)&&fs.existsSync(path.join(runDir,'history-status.json'))){injected=true;fs.writeFileSync(late,'export const vulnerable = true\n');}
return options===undefined?lstat(candidate):lstat(candidate,options);
}) as typeof fs.lstatSync);
try{await capture(repo,runDir);}catch(error){failure=error;}finally{patched.mockRestore();}
expect(injected).toBe(true);expect(fs.readFileSync(late,'utf8')).toContain('vulnerable');expect(failure).toMatchObject({code:'SNAPSHOT_RACE'});expect(fs.existsSync(path.join(runDir,'snapshot.json'))).toBe(false);
});
test.skipIf(process.platform==='win32')('binds the audited repository root while source is copied',async()=>{
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-snapshot-root-swap-'));roots.push(root);const repo=path.join(root,'repo'),parked=path.join(root,'parked'),decoy=path.join(root,'decoy'),gitDir=path.join(root,'gitdir'),runDir=path.join(root,'run'),tracked=path.join(repo,'tracked.txt');fs.mkdirSync(repo);fs.mkdirSync(runDir,{mode:0o700});
const run=(...args:string[])=>{const result=spawnSync(trustedGit(),args,{encoding:'utf8',env:gitEnvironment(root),timeout:30_000});if(result.status)throw new Error(result.stderr);};
run('init','-q',`--separate-git-dir=${gitDir}`,repo);run('-C',repo,'config','user.email','fixture@example.test');run('-C',repo,'config','user.name','Fixture');fs.writeFileSync(tracked,'secure original source\n');run('-C',repo,'add','tracked.txt');run('-C',repo,'commit','-qm','base');fs.mkdirSync(decoy);fs.writeFileSync(path.join(decoy,'.git'),`gitdir: ${gitDir}\n`);fs.writeFileSync(path.join(decoy,'tracked.txt'),'vulnerable decoy source\n');
const lstat=fs.lstatSync;let seen=0,injected=false,failure:unknown;
const patched=spyOn(fs,'lstatSync').mockImplementation(((candidate:any,options?:any)=>{if(path.resolve(String(candidate))===tracked&&++seen===2){injected=true;fs.renameSync(repo,parked);fs.renameSync(decoy,repo);}return options===undefined?lstat(candidate):lstat(candidate,options);}) as typeof fs.lstatSync);
try{await capture(repo,runDir);}catch(error){failure=error;}finally{patched.mockRestore();if(injected){fs.renameSync(repo,decoy);fs.renameSync(parked,repo);}}
expect(injected).toBe(true);expect(failure).toMatchObject({code:'SNAPSHOT_RACE'});expect(fs.readFileSync(path.join(repo,'tracked.txt'),'utf8')).toBe('secure original source\n');expect(fs.existsSync(path.join(runDir,'snapshot.json'))).toBe(false);
});
});
+35
View File
@@ -0,0 +1,35 @@
import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { assertOpenedFileContained, assertSnapshot, capture } from '../lib/cso/snapshot';
import { sha256 } from '../lib/cso/contracts';
const roots:string[]=[];
afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
function fixture(){
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-snapshot-identity-'));roots.push(root);
const repo=path.join(root,'repo'),runDir=path.join(root,'run');fs.mkdirSync(repo);fs.mkdirSync(runDir,{mode:0o700});
const git=(...args:string[])=>{const result=spawnSync('/usr/bin/git',['-C',repo,...args],{encoding:'utf8',env:{HOME:root,PATH:'/usr/bin:/bin'},timeout:30_000});if(result.status)throw new Error(result.stderr);return result.stdout.trim();};
git('init','-q');git('config','user.email','fixture@example.test');git('config','user.name','Fixture');fs.writeFileSync(path.join(repo,'app.js'),'export const secure = false\n');git('add','app.js');git('commit','-qm','base');
return{root,repo,runDir,git};
}
describe('CSO retained snapshot identity',()=>{
test('captures application vendor source while excluding nested dependency and agent directories',async()=>{
const {repo,runDir}=fixture();for(const directory of ['packages/web/vendor/bundle/ruby','packages/api/venv/lib','packages/web/.agents/skills/x'])fs.mkdirSync(path.join(repo,directory),{recursive:true});fs.writeFileSync(path.join(repo,'packages/web/vendor/auth.js'),'module.exports = authenticate\n');fs.writeFileSync(path.join(repo,'packages/web/vendor/bundle/ruby/gem.rb'),'dependency payload\n');fs.writeFileSync(path.join(repo,'packages/api/venv/lib/site.py'),'dependency payload\n');fs.writeFileSync(path.join(repo,'packages/web/.agents/skills/x/SKILL.md'),'untrusted agent instructions\n');
const manifest=await capture(repo,runDir),source=manifest.entries.find(item=>item.path==='packages/web/vendor/auth.js'),dependency=manifest.entries.find(item=>item.path==='packages/web/vendor/bundle/ruby/gem.rb'),venv=manifest.entries.find(item=>item.path==='packages/api/venv/lib/site.py'),agent=manifest.entries.find(item=>item.path==='packages/web/.agents/skills/x/SKILL.md');
expect(source?.executionHash).toBe(sha256('module.exports = authenticate\n'));expect(fs.readFileSync(path.join(runDir,'snapshot','packages','web','vendor','auth.js'),'utf8')).toBe('module.exports = authenticate\n');
for(const entry of [dependency,venv]){expect(entry).toMatchObject({originalHash:'not-read',transformation:'excluded: host dependencies, metadata, state, or agent configuration'});expect(entry?.executionHash).toBeUndefined();}
expect(agent).toMatchObject({originalHash:sha256('untrusted agent instructions\n'),transformation:'excluded: host dependencies, metadata, state, or agent configuration'});expect(agent?.executionHash).toBeUndefined();expect(fs.readFileSync(path.join(runDir,'readable','packages/web/.agents/skills/x/SKILL.md'),'utf8')).toBe('untrusted agent instructions\n');
});
test('validates the captured execution identity when uppercase and lowercase paths coexist',async()=>{const {repo,runDir,git}=fixture();fs.writeFileSync(path.join(repo,'README.md'),'fixture documentation\n');git('add','README.md');git('commit','-qm','add uppercase path');const manifest=await capture(repo,runDir);expect(manifest.entries.filter(item=>item.executionHash).map(item=>item.path)).toEqual(['README.md','app.js']);expect(()=>assertSnapshot(runDir,manifest)).not.toThrow();});
test('binds original, sanitized, and opaque path identities',async()=>{const {repo,runDir}=fixture(),manifest=await capture(repo,runDir);assertSnapshot(runDir,manifest);const changedEntry=structuredClone(manifest);changedEntry.entries[0].originalHash='a'.repeat(64);expect(()=>assertSnapshot(runDir,changedEntry)).toThrow('original identity');const changedRoot=structuredClone(manifest);changedRoot.originalHash='b'.repeat(64);expect(()=>assertSnapshot(runDir,changedRoot)).toThrow('original identity');const changedPathId=structuredClone(manifest);changedPathId.entries[0].pathId='c'.repeat(32);expect(()=>assertSnapshot(runDir,changedPathId)).toThrow('invalid source entry');});
test.skipIf(process.platform==='win32')('preserves retained source modes under the required restrictive umask',async()=>{const {repo,runDir,git}=fixture(),script=path.join(repo,'tool.sh');fs.writeFileSync(script,'#!/bin/sh\nexit 0\n');fs.chmodSync(path.join(repo,'app.js'),0o644);fs.chmodSync(script,0o755);git('add','app.js','tool.sh');git('commit','-qm','add executable input');const previous=process.umask(0o077);let manifest;try{manifest=await capture(repo,runDir);}finally{process.umask(previous);}expect(manifest.entries.find(item=>item.path==='app.js')?.mode).toBe(0o644);expect(manifest.entries.find(item=>item.path==='tool.sh')?.mode).toBe(0o755);expect(fs.statSync(path.join(runDir,'snapshot','app.js')).mode&0o777).toBe(0o644);expect(fs.statSync(path.join(runDir,'snapshot','tool.sh')).mode&0o777).toBe(0o755);assertSnapshot(runDir,manifest);});
test('rejects a replaced snapshot root before following it',async()=>{const {repo,runDir}=fixture(),manifest=await capture(repo,runDir),snapshot=path.join(runDir,'snapshot'),replacement=path.join(runDir,'snapshot-replacement');fs.renameSync(snapshot,replacement);fs.symlinkSync(replacement,snapshot);expect(()=>assertSnapshot(runDir,manifest)).toThrow('private owned directory');});
test.skipIf(process.platform==='win32')('detects an ancestor symlink swap after a source file is opened',()=>{const {root,repo}=fixture(),outside=path.join(root,'outside'),alias=path.join(repo,'swapped'),secret=path.join(outside,'secret.txt');fs.mkdirSync(outside);fs.writeFileSync(secret,'outside source must not be captured\n');fs.symlinkSync(outside,alias);const full=path.join(alias,'secret.txt'),fd=fs.openSync(full,fs.constants.O_RDONLY);try{expect(()=>assertOpenedFileContained(repo,full,fd,fs.fstatSync(fd))).toThrow('escaped the audited root');}finally{fs.closeSync(fd);}});
test.skipIf(process.platform==='win32')('rejects an untracked FIFO before opening it',async()=>{const {repo,runDir}=fixture(),fifo=path.join(repo,'request.pipe'),created=spawnSync('/usr/bin/mkfifo',[fifo],{encoding:'utf8',timeout:5_000});expect(created.status).toBe(0);const started=Date.now();await expect(capture(repo,runDir)).rejects.toThrow('Symlink or special source file');expect(Date.now()-started).toBeLessThan(2_000);});
test('withholds a large regular source file without aborting the bounded snapshot',async()=>{const {repo,runDir}=fixture(),large=Buffer.alloc(1024*1024+1,0x61);fs.writeFileSync(path.join(repo,'large.txt'),large);const manifest=await capture(repo,runDir),entry=manifest.entries.find(item=>item.path==='large.txt');expect(entry).toMatchObject({bytes:large.length,originalHash:sha256(large),transformation:'withheld: exceeds the 1 MiB redacting-reader limit'});expect(entry?.executionHash).toBeUndefined();expect(fs.existsSync(path.join(runDir,'snapshot','large.txt'))).toBe(false);assertSnapshot(runDir,manifest);});
test('binds a recheck snapshot to the exact captured descendant commit',async()=>{const {repo,runDir,git}=fixture(),ancestor=git('rev-parse','HEAD');git('checkout','--orphan','unrelated');fs.writeFileSync(path.join(repo,'app.js'),'export const unrelated = true\n');git('add','app.js');git('commit','-qm','unrelated root');await expect(capture(repo,runDir,undefined,ancestor)).rejects.toThrow('not a descendant');});
});
+213
View File
@@ -0,0 +1,213 @@
import { afterAll, afterEach, describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';import * as os from 'node:os';import * as path from 'node:path';import { spawn, spawnSync } from 'node:child_process';
import { assertSnapshot, capture } from '../lib/cso/snapshot';import { CsoError } from '../lib/cso/contracts';import { runProcess, sanitizeForJson, sanitizeHelperForJson } from '../lib/cso/process';import { discardAtomicNoReplaceTemp, finalizeReplayTemporary, loadReport, newRun, privateRoot, readJson, retention, saveReport, secureDirectory, stateRoot, withLock, writeHelperJson, writeJson } from '../lib/cso/state';
const roots:string[]=[];const tmp=()=>{const p=fs.mkdtempSync(path.join(os.tmpdir(),'cso-snapshot-'));roots.push(p);return p;};afterEach(()=>{for(const p of roots.splice(0))fs.rmSync(p,{recursive:true,force:true});});
const originalState=process.env.GSTACK_HOME,state=fs.mkdtempSync(path.join(os.tmpdir(),'cso-state-'));process.env.GSTACK_HOME=state;afterAll(()=>{if(originalState===undefined)delete process.env.GSTACK_HOME;else process.env.GSTACK_HOME=originalState;fs.rmSync(state,{recursive:true,force:true});});
function git(repo:string,...args:string[]){const r=spawnSync('/usr/bin/git',['-C',repo,...args],{encoding:'utf8',env:{PATH:'/usr/bin:/bin',HOME:repo},timeout:30_000});if(r.status)throw new Error(r.stderr);return r.stdout;}
function repo(){const p=tmp();git(p,'init','-q');git(p,'config','user.email','fixture@example.test');git(p,'config','user.name','Fixture');fs.writeFileSync(path.join(p,'tracked.txt'),'base\n');git(p,'add','tracked.txt');git(p,'commit','-qm','base');return p;}
async function waitForPath(file:string,timeout=5000):Promise<void>{const deadline=Date.now()+timeout;while(!fs.existsSync(file)){if(Date.now()>=deadline)throw new Error(`Timed out waiting for ${path.basename(file)}`);await Bun.sleep(5);}}
function childExit(child:ReturnType<typeof spawn>,timeout=5000):Promise<number|null>{return new Promise((resolve,reject)=>{if(child.exitCode!==null){resolve(child.exitCode);return;}const timer=setTimeout(()=>reject(new Error(`Child ${child.pid} did not exit`)),timeout);child.once('error',error=>{clearTimeout(timer);reject(error);});child.once('close',code=>{clearTimeout(timer);resolve(code);});});}
function stopChildren(children:Array<ReturnType<typeof spawn>>):void{for(const child of children){if(!child.pid||child.exitCode!==null)continue;try{process.kill(child.pid,'SIGCONT');}catch{}try{process.kill(child.pid,'SIGKILL');}catch{}}}
function leaseContenderSource():string{return `import fs from 'node:fs';import * as fsModule from 'node:fs';import * as cryptoModule from 'node:crypto';import path from 'node:path';import {spyOn} from 'bun:test';
const dir=process.env.RACE_DIR!,barrier=process.env.BARRIER!,id=process.env.RACE_ID!,mode=process.env.RACE_MODE??'normal',token=process.env.RACE_TOKEN,leases=path.join(dir,'.mutation-lock-leases'),wait=new Int32Array(new SharedArrayBuffer(4));let triggered=false;
const write=fs.writeFileSync.bind(fs),mark=(name,value='ready')=>write(path.join(barrier,name),value,{flag:'wx'}),waitFor=(name)=>{const deadline=Date.now()+5000;while(!fs.existsSync(path.join(barrier,name))){if(Date.now()>=deadline)throw new Error('barrier timeout: '+name);Atomics.wait(wait,0,0,1);}};
if(token){const random=cryptoModule.randomBytes.bind(cryptoModule);spyOn(cryptoModule,'randomBytes').mockImplementation(((size,...args)=>size===16?Buffer.from(token,'hex'):random(size,...args)) as typeof cryptoModule.randomBytes);}
const read=fs.readdirSync.bind(fs);spyOn(fsModule,'readdirSync').mockImplementation(((candidate,...args)=>{const result=read(candidate,...args),names=result as any[];if(mode==='pause-candidate'&&!triggered&&String(candidate)===leases&&names.some(name=>token?String(name)===token+'.json':/^[a-f0-9]{32}\.json$/.test(String(name)))){triggered=true;mark('paused-'+id);process.kill(process.pid,'SIGSTOP');}if(mode==='pause-later-scan'&&!triggered&&String(candidate)===leases&&names.filter(name=>String(name).endsWith('.decision')).length>=2&&!names.some(name=>String(name).includes('.active.'))){triggered=true;mark('paused-'+id);process.kill(process.pid,'SIGSTOP');}return result;}) as typeof fs.readdirSync);
spyOn(fsModule,'writeFileSync').mockImplementation(((target,data,...args)=>{const name=path.basename(String(target));if(name.includes('.decision.tmp.')){let decision;try{decision=JSON.parse(String(data));}catch{}if(decision?.kind==='ticket'&&decision.token===token){try{mark('choice-'+id,decision.ticket);}catch{}if(mode==='pause-before-decision'&&!triggered){triggered=true;mark('paused-'+id);process.kill(process.pid,'SIGSTOP');}}if(decision?.kind==='withdraw'&&mode==='pause-before-withdraw'&&!triggered){triggered=true;mark('paused-'+id);process.kill(process.pid,'SIGSTOP');}}return write(target,data,...args);}) as typeof fs.writeFileSync);
const link=fs.linkSync.bind(fs);spyOn(fsModule,'linkSync').mockImplementation(((source,target)=>{const name=path.basename(String(target));if(mode==='wait-active'&&!triggered&&name.includes('.active.')){triggered=true;mark('doorway-'+id);waitFor('paused-H');}return link(source,target);}) as typeof fs.linkSync);
const unlink=fs.unlinkSync.bind(fs);spyOn(fsModule,'unlinkSync').mockImplementation(((target)=>{const result=unlink(target),name=path.basename(String(target)),candidateToken=name.endsWith('.json')?name.slice(0,-5):'';if((mode==='pause-decision'||mode==='pause-ticket')&&!triggered&&name.includes('.decision.tmp.')&&(!token||name.startsWith(token+'.decision.tmp.'))){triggered=true;mark('paused-'+id);process.kill(process.pid,'SIGSTOP');}if((mode==='pause-after-candidate-cleanup'||mode==='pause-withdraw-cleanup')&&!triggered&&/^[a-f0-9]{32}$/.test(candidateToken)&&(!token||candidateToken===token)&&fs.existsSync(path.join(leases,candidateToken+'.decision'))){triggered=true;mark('paused-'+id);process.kill(process.pid,'SIGSTOP');}return result;}) as typeof fs.unlinkSync);
const {withLock}=await import(${JSON.stringify(path.resolve(import.meta.dir,'../lib/cso/state.ts'))});let outcome='success';try{withLock(dir,()=>{const active=path.join(barrier,'work-active');try{write(active,id,{flag:'wx'});}catch{fs.appendFileSync(path.join(barrier,'overlap'),'overlap\\n');}mark('entered-'+id);if(process.env.RACE_HOLD==='1')waitFor('release-'+id);try{if(fs.readFileSync(active,'utf8')===id)fs.unlinkSync(active);}catch{}});}catch(error){outcome=String(error?.code??error);}write(path.join(barrier,'result-'+id),outcome);`;}
describe('CSO dirty snapshot boundary',()=>{
test('captures tracked modifications and nonignored untracked source without changing Git state',async()=>{const p=repo();fs.writeFileSync(path.join(p,'tracked.txt'),'dirty\n');fs.writeFileSync(path.join(p,'new.txt'),'untracked\n');fs.writeFileSync(path.join(p,'.gitignore'),'ignored.txt\n');fs.writeFileSync(path.join(p,'ignored.txt'),'ignored\n');const before=git(p,'status','--porcelain=v1','-z'),run=newRun(p),m=await capture(p,run.dir,'HEAD');expect(fs.readFileSync(path.join(run.dir,'snapshot','tracked.txt'),'utf8')).toBe('dirty\n');expect(fs.readFileSync(path.join(run.dir,'snapshot','new.txt'),'utf8')).toBe('untracked\n');expect(fs.existsSync(path.join(run.dir,'snapshot','ignored.txt'))).toBe(false);expect(git(p,'status','--porcelain=v1','-z')).toBe(before);expect(m.changedPaths).toContain('new.txt');});
test('admits source membership and time before an unbounded startup walk',async()=>{const p=repo();fs.writeFileSync(path.join(p,'second.txt'),'second\n');const tooMany=newRun(p);await expect(capture(p,tooMany.dir,undefined,undefined,{maxEntries:1})).rejects.toMatchObject({code:'MISSING_INPUT'});expect(fs.existsSync(path.join(tooMany.dir,'snapshot'))).toBe(false);const expired=newRun(p);await expect(capture(p,expired.dir,undefined,undefined,{deadlineMs:Date.now()-1})).rejects.toMatchObject({code:'DEADLINE'});expect(fs.existsSync(path.join(expired.dir,'snapshot'))).toBe(false);});
test('includes chmod-only changes in diff scope',async()=>{const p=repo();fs.chmodSync(path.join(p,'tracked.txt'),0o755);const manifest=await capture(p,newRun(p).dir,'HEAD');expect(manifest.changedPaths).toContain('tracked.txt');});
test('does not invoke configured clean filters, hooks, fsmonitor, or PATH shims',async()=>{const p=repo(),marker=path.join(p,'marker');fs.writeFileSync(path.join(p,'.gitattributes'),'*.txt filter=hostile\n');git(p,'config','filter.hostile.clean',`/bin/sh -c 'touch ${marker}; cat'`);git(p,'config','core.fsmonitor',`/bin/sh -c 'touch ${marker}'`);const fake=path.join(p,'bin');fs.mkdirSync(fake);fs.writeFileSync(path.join(fake,'git'),`#!/bin/sh\ntouch '${marker}'\nexit 99\n`,{mode:0o755});const old=process.env.PATH;process.env.PATH=`${fake}:${old}`;try{await capture(p,newRun(p).dir,'HEAD');}finally{process.env.PATH=old;}expect(fs.existsSync(marker)).toBe(false);});
test('rejects symlinks and hard links as execution inputs',async()=>{const p=repo();fs.symlinkSync('/etc/passwd',path.join(p,'escape'));expect(capture(p,newRun(p).dir)).rejects.toThrow();fs.unlinkSync(path.join(p,'escape'));fs.linkSync(path.join(p,'tracked.txt'),path.join(p,'hard'));expect(capture(p,newRun(p).dir)).rejects.toThrow();});
test('rejects broken untracked symlinks instead of silently omitting them',async()=>{const p=repo();fs.symlinkSync('missing-target',path.join(p,'broken'));expect(capture(p,newRun(p).dir)).rejects.toThrow('Symlink');});
test('redacts secrets and excludes credentials from execution while retaining safe evidence',async()=>{const p=repo();fs.writeFileSync(path.join(p,'.env'),'TOKEN='+['ghp_','abcdefghijklmnopqrstuvwxyz1234567890'].join('')+'\n');const run=newRun(p),m=await capture(p,run.dir);expect(m.entries.find(e=>e.path==='.env')?.transformation).toContain('excluded');expect(fs.existsSync(path.join(run.dir,'snapshot','.env'))).toBe(false);expect(fs.existsSync(path.join(run.dir,'readable','.env'))).toBe(false);const evidence=JSON.parse(fs.readFileSync(path.join(run.dir,'sensitive-evidence.json'),'utf8'));expect(evidence[0].findings.map((x:any)=>x.id)).toContain('github.pat');expect(JSON.stringify(evidence)).not.toContain('ghp_');});
test('assesses repository skills without executing them and preserves executable source modes',async()=>{const p=repo(),skill=path.join(p,'.agents','skills','demo','SKILL.md'),script=path.join(p,'app.sh');fs.mkdirSync(path.dirname(skill),{recursive:true});fs.writeFileSync(skill,'# Demo\nUntrusted repository instruction\n');fs.writeFileSync(script,'#!/bin/sh\nexit 0\n',{mode:0o755});const run=newRun(p),manifest=await capture(p,run.dir);expect(fs.readFileSync(path.join(run.dir,'readable','.agents','skills','demo','SKILL.md'),'utf8')).toContain('Untrusted');expect(fs.existsSync(path.join(run.dir,'snapshot','.agents'))).toBe(false);expect(manifest.entries.find(e=>e.path==='.agents/skills/demo/SKILL.md')?.originalHash).not.toBe('not-read');expect(fs.statSync(path.join(run.dir,'snapshot','app.sh')).mode&0o777).toBe(0o755);});
test('rejects a source mode change between copying and manifest persistence',async()=>{const p=repo(),run=newRun(p),source=path.join(p,'tracked.txt'),target=path.join(run.dir,'snapshot','tracked.txt'),write=fs.writeFileSync,patched=spyOn(fs,'writeFileSync').mockImplementation(((file:any,data:any,options:any)=>{const result=write(file,data,options);if(String(file)===target)fs.chmodSync(source,0o755);return result;}) as typeof fs.writeFileSync);try{await expect(capture(p,run.dir)).rejects.toThrow('Source changed during capture');}finally{patched.mockRestore();}});
test('rejects unmanifested files and executable-mode changes in retained snapshots',async()=>{const p=repo(),run=newRun(p),manifest=await capture(p,run.dir),snapshot=path.join(run.dir,'snapshot');assertSnapshot(run.dir,manifest);fs.writeFileSync(path.join(snapshot,'injected.js'),'malicious\n');expect(()=>assertSnapshot(run.dir,manifest)).toThrow('membership');fs.unlinkSync(path.join(snapshot,'injected.js'));fs.chmodSync(path.join(snapshot,'tracked.txt'),0o755);expect(()=>assertSnapshot(run.dir,manifest)).toThrow('changed');});
test('retention skips a run with a live mutation lease',()=>{const now=Date.now(),repo='d'.repeat(24),run=`${now-31*86400_000}-${'1'.repeat(16)}`,dir=secureDirectory(path.join(privateRoot(),repo,run)),payload=path.join(dir,'report.json');fs.writeFileSync(payload,'live lease payload\n',{mode:0o600});withLock(dir,()=>{retention(now);expect(fs.existsSync(payload)).toBe(true);});retention(now);expect(fs.existsSync(dir)).toBe(false);});
test('bounded retention stops before destructive cleanup when its maintenance budget is exhausted',()=>{const now=Date.now(),repo='e'.repeat(24),run=`${now-31*86400_000}-${'8'.repeat(16)}`,dir=secureDirectory(path.join(privateRoot(),repo,run));fs.writeFileSync(path.join(dir,'report.json'),'expired private state\n',{mode:0o600});const result=retention(now,{deadlineMs:0,maxEntries:1});expect(result).toEqual({complete:false,visited:0});expect(fs.existsSync(dir)).toBe(true);retention(now);expect(fs.existsSync(dir)).toBe(false);});
test('bounded retention completes discovery before deleting any run',()=>{const now=Date.now(),repo='f'.repeat(24),run=`${now-31*86400_000}-${'9'.repeat(16)}`,dir=secureDirectory(path.join(privateRoot(),repo,run));fs.writeFileSync(path.join(dir,'report.json'),'expired private state\n',{mode:0o600});const result=retention(now,{deadlineMs:now+60_000,maxEntries:6});expect(result).toEqual({complete:false,visited:6});expect(fs.existsSync(dir)).toBe(true);retention(now);expect(fs.existsSync(dir)).toBe(false);});
test('bounded retention resumes an interrupted tree deletion without one recursive walk',()=>{const previous=process.env.GSTACK_HOME,isolate=tmp();process.env.GSTACK_HOME=isolate;try{const now=Date.now(),repo='1'.repeat(24),run=`${now-31*86400_000}-${'a'.repeat(16)}`,repoDir=path.join(privateRoot(),repo),dir=secureDirectory(path.join(repoDir,run)),snapshot=secureDirectory(path.join(dir,'snapshot'));for(let index=0;index<20;index++)fs.writeFileSync(path.join(snapshot,`${index}.txt`),'private state\n',{mode:0o600});const result=retention(now,{deadlineMs:now+60_000,maxEntries:30});expect(result).toEqual({complete:false,visited:30});expect(fs.existsSync(dir)).toBe(false);expect(fs.readdirSync(repoDir).some(name=>name.startsWith(`.retired-${run}-`))).toBe(true);retention(now);expect(fs.readdirSync(repoDir).some(name=>name.startsWith(`.retired-${run}-`))).toBe(false);}finally{if(previous===undefined)delete process.env.GSTACK_HOME;else process.env.GSTACK_HOME=previous;}});
test('repair bundles expire from their authenticated bundle time, independent of run age',()=>{const now=Date.now(),repo='c'.repeat(24),run=`${now-31*86400_000}-${'2'.repeat(16)}`,dir=secureDirectory(path.join(privateRoot(),repo,run)),bundles=secureDirectory(path.join(dir,'bundles')),expiredId='3'.repeat(32),retainedId='4'.repeat(32),record=(id:string,created:number)=>{const createdAt=new Date(created).toISOString();return{schemaVersion:3,id,runId:run,createdAt,expiresAt:new Date(created+30*86400_000).toISOString(),verification:{id,runId:run,createdAt}};};writeHelperJson(path.join(bundles,`${expiredId}.json`),record(expiredId,now-31*86400_000));writeHelperJson(path.join(bundles,`${retainedId}.json`),record(retainedId,now-86400_000));retention(now);expect(fs.existsSync(path.join(bundles,`${expiredId}.json`))).toBe(false);expect(fs.existsSync(path.join(bundles,`${retainedId}.json`))).toBe(true);expect(fs.existsSync(dir)).toBe(true);retention(now+30*86400_000);expect(fs.existsSync(dir)).toBe(false);});
test('replay temporary state survives cleanup failure until every detached watchdog acknowledges cleanup',()=>{const now=Date.now(),repo='b'.repeat(24),run=`${now}-${'5'.repeat(16)}`,dir=secureDirectory(path.join(privateRoot(),repo,run)),control=secureDirectory(path.join(dir,'supervision','repair-attempt')),docker=secureDirectory(path.join(dir,'preparation-execution','offline-attempt'));fs.writeFileSync(path.join(control,'attempt.ready'),'ready\n',{mode:0o600});fs.writeFileSync(path.join(control,'attempt.terminal'),'normal cleanup complete\n',{mode:0o600});fs.writeFileSync(path.join(docker,'watchdog.ready'),'ready\n',{mode:0o600});fs.writeFileSync(path.join(docker,'watchdog.event'),'cleanup incomplete; retrying exact journaled resources\n',{mode:0o600});finalizeReplayTemporary(dir);expect(fs.existsSync(path.join(dir,'.ephemeral-replay.json'))).toBe(true);retention(now);expect(fs.existsSync(control)).toBe(true);expect(fs.existsSync(docker)).toBe(true);fs.writeFileSync(path.join(control,'attempt.stopped'),'normal cleanup acknowledged\n',{mode:0o600});retention(now);expect(fs.existsSync(dir)).toBe(true);fs.writeFileSync(path.join(docker,'watchdog.stopped'),'normal cleanup acknowledged\n',{mode:0o600});retention(now);expect(fs.existsSync(dir)).toBe(false);});
test('retention recovers a hidden run retirement left by a dead helper',()=>{const now=Date.now(),repo='a'.repeat(24),run=`${now-31*86400_000}-${'6'.repeat(16)}`,retired=secureDirectory(path.join(privateRoot(),repo,`.retired-${run}-${'7'.repeat(32)}`));fs.writeFileSync(path.join(retired,'payload'),'private state\n',{mode:0o600});retention(now);expect(fs.existsSync(retired)).toBe(false);});
});
describe('private state and process output',()=>{
test('state root mirrors the plugin convention and private writes use restrictive modes',()=>{const base=tmp(),root=stateRoot({HOME:base,GSTACK_HOME:'',CLAUDE_PLUGIN_ROOT:'',CLAUDE_PLUGIN_DATA:''});expect(root).toBe(path.join(base,'.gstack'));const dir=secureDirectory(path.join(base,'state')),file=path.join(dir,'x.json');writeJson(file,{token:'safe'});expect(fs.statSync(dir).mode&0o777).toBe(0o700);expect(fs.statSync(file).mode&0o777).toBe(0o600);});
test('private root preserves caller-owned container modes and secures only its own boundary',()=>{if(process.platform==='win32')return;const old=process.env.GSTACK_HOME,base=tmp(),selected=path.join(base,'selected'),security=path.join(selected,'security'),sentinel=path.join(selected,'keep');fs.mkdirSync(security,{recursive:true});fs.chmodSync(selected,0o755);fs.chmodSync(security,0o751);fs.writeFileSync(sentinel,'kept');const selectedInode=fs.statSync(selected).ino,securityInode=fs.statSync(security).ino;process.env.GSTACK_HOME=selected;try{expect(privateRoot()).toBe(path.join(security,'cso'));expect(privateRoot()).toBe(path.join(security,'cso'));expect(fs.statSync(selected).mode&0o777).toBe(0o755);expect(fs.statSync(security).mode&0o777).toBe(0o751);expect(fs.statSync(selected).ino).toBe(selectedInode);expect(fs.statSync(security).ino).toBe(securityInode);expect(fs.readFileSync(sentinel,'utf8')).toBe('kept');expect(fs.statSync(path.join(security,'cso')).mode&0o777).toBe(0o700);}finally{if(old===undefined)delete process.env.GSTACK_HOME;else process.env.GSTACK_HOME=old;}});
test('private root creates a missing selected container privately',()=>{const old=process.env.GSTACK_HOME,base=tmp(),selected=path.join(base,'missing');process.env.GSTACK_HOME=selected;try{expect(privateRoot()).toBe(path.join(selected,'security','cso'));for(const directory of [selected,path.join(selected,'security'),path.join(selected,'security','cso')])expect(fs.statSync(directory).mode&0o777).toBe(0o700);}finally{if(old===undefined)delete process.env.GSTACK_HOME;else process.env.GSTACK_HOME=old;}});
test('private root rejects unsafe or filesystem-root containers without repairing them',()=>{if(process.platform==='win32')return;const old=process.env.GSTACK_HOME,base=tmp(),selected=path.join(base,'unsafe');fs.mkdirSync(selected);fs.chmodSync(selected,0o770);const inode=fs.statSync(selected).ino;try{process.env.GSTACK_HOME=selected;expect(()=>privateRoot()).toThrow('group- or world-writable ancestor');expect(fs.statSync(selected).mode&0o777).toBe(0o770);expect(fs.statSync(selected).ino).toBe(inode);expect(fs.existsSync(path.join(selected,'security'))).toBe(false);process.env.GSTACK_HOME=path.parse(selected).root;const chmod=spyOn(fs,'chmodSync');try{expect(()=>privateRoot()).toThrow('filesystem root');expect(chmod.mock.calls.some(call=>path.resolve(String(call[0]))===path.parse(selected).root)).toBe(false);}finally{chmod.mockRestore();}}finally{if(old===undefined)delete process.env.GSTACK_HOME;else process.env.GSTACK_HOME=old;}});
test('concurrent first-run directory creation accepts only the expected EEXIST race',()=>{const base=tmp(),target=path.join(base,'first','second'),first=path.join(base,'first'),mkdir=fs.mkdirSync;let raced=false;const patched=spyOn(fs,'mkdirSync').mockImplementation(((candidate:any,options:any)=>{if(String(candidate)===first&&!raced){raced=true;mkdir(candidate,options);const error:any=new Error('created concurrently');error.code='EEXIST';throw error;}return mkdir(candidate,options);}) as typeof fs.mkdirSync);try{expect(secureDirectory(target)).toBe(target);expect(raced).toBe(true);expect(fs.statSync(target).mode&0o777).toBe(0o700);}finally{patched.mockRestore();}});
test('private state rejects a non-sticky group-writable ancestor',()=>{if(process.platform==='win32')return;const base=tmp();fs.chmodSync(base,0o770);expect(()=>secureDirectory(path.join(base,'private'))).toThrow('group- or world-writable ancestor');});
test('split process output is joined and redacted before return',async()=>{const dir=tmp(),r=await runProcess('/bin/sh',['-c',"printf 'ghp_abcdefghijklmnop'; printf 'qrstuvwxyz1234567890' >&2"],{cwd:dir,env:{PATH:'/usr/bin:/bin'}});expect(r.stdout+r.stderr).not.toContain('ghp_');expect(r.stdout.toLowerCase()).toContain('redacted');});
test('alternating stdout and stderr chunks cannot split a secret marker',async()=>{const dir=tmp(),r=await runProcess('/bin/sh',['-c',"printf g; sleep .02; printf hp_abcdefghijklmnop >&2; sleep .02; printf qrstuvwxyz1234567890"],{cwd:dir,env:{PATH:'/usr/bin:/bin'}});expect(r.stdout+r.stderr).not.toContain('abcdefghijklmnop');expect(r.stdout.toLowerCase()).toContain('redacted');});
test('marker-only private keys withhold the whole payload',async()=>{const dir=tmp();expect(runProcess('/bin/sh',['-c',"printf '%s' \"$1\"",'sh',['-----BEGIN ','PRIVATE KEY-----\nraw\n-----END ','PRIVATE KEY-----'].join('')],{cwd:dir,env:{PATH:'/usr/bin:/bin'}})).rejects.toMatchObject({code:'REDACTION_FAILED'});});
test('untrusted hash-like keys do not bypass structured redaction',()=>{const dir=tmp(),file=path.join(dir,'value.json');writeJson(file,{payloadHash:['ghp_','abcdefghijklmnopqrstuvwxyz1234567890'].join('')});expect(fs.readFileSync(file,'utf8')).not.toContain('ghp_');});
test('only schema-bound helper metadata bypasses content redaction',()=>{const token='1abcdefabcdefabcdefabcdefabcdefa',hash='a'.repeat(64),untrusted=sanitizeForJson({id:token,findingId:token,sourceHash:hash}) as any;expect(untrusted.id).not.toBe(token);expect(untrusted.findingId).not.toBe(token);const trusted=sanitizeHelperForJson({id:token,findingId:token,artifactId:token,reviewArtifactId:token,bundleId:token,sourceHash:hash,preparedManifestHash:hash}) as any;expect(trusted).toEqual({id:token,findingId:token,artifactId:token,reviewArtifactId:token,bundleId:token,sourceHash:hash,preparedManifestHash:hash});const dir=tmp(),file=path.join(dir,'claim.json');writeHelperJson(file,{findingId:token,outcome:'open'});expect(readJson(file)).toEqual({findingId:token,outcome:'open'});});
test('oversized report growth is rejected before replacing the last readable report',()=>{const dir=tmp(),report:any={schemaVersion:3,runId:'1'.repeat(13)+'-'+'a'.repeat(16),repoId:'b'.repeat(24),createdAt:new Date().toISOString(),deadline:new Date(Date.now()+60_000).toISOString(),status:'running',completeness:'not assessed',policy:{mode:'daily',scope:'default',diff:false,base:'origin/main',offline:true,budgetSeconds:600,maxWorkers:3,maxRepairs:3},source:{root:'/source',snapshotHash:'c'.repeat(64),originalHash:'d'.repeat(64)},application:{actors:[],assets:[],entrypoints:[],tenantBoundaries:[],sensitiveOperations:[],invariants:[]},coverage:[],findings:[],gaps:[],events:[]};saveReport(dir,report);const original=fs.readFileSync(path.join(dir,'report.json'),'utf8');report.events=Array.from({length:4000},(_,i)=>({at:new Date().toISOString(),kind:'evidence',message:`${i}:`+'x'.repeat(300)}));expect(()=>saveReport(dir,report)).toThrow('previous artifact was preserved');expect(fs.readFileSync(path.join(dir,'report.json'),'utf8')).toBe(original);expect(loadReport(dir).status).toBe('running');});
test('a fully published dead legacy owner migrates to the immutable lease protocol',()=>{const dir=tmp(),lock=path.join(dir,'.mutation-lock');fs.mkdirSync(lock);fs.writeFileSync(path.join(lock,'owner.json'),JSON.stringify({pid:2147483647,token:'old',createdAt:0,expiresAt:0}));expect(withLock(dir,()=>42)).toBe(42);expect(fs.lstatSync(lock).isFile()).toBe(true);expect(JSON.parse(fs.readFileSync(lock,'utf8')).protocol).toBe('immutable-lease-set-v3');expect(fs.readdirSync(path.join(dir,'.mutation-lock-leases'))).toEqual([]);});
test('a dead legacy migration claim is recovered after an interrupted migration',()=>{const dir=tmp(),lock=path.join(dir,'.mutation-lock'),claim=path.join(lock,'.v3-migration'),token='b'.repeat(32);fs.mkdirSync(lock);fs.writeFileSync(path.join(lock,'owner.json'),JSON.stringify({pid:2147483647,token:'old',createdAt:0}));fs.writeFileSync(claim,JSON.stringify({pid:2147483647,token,createdAt:0})+'\n',{mode:0o600,flag:'wx'});expect(withLock(dir,()=>42)).toBe(42);expect(fs.lstatSync(lock).isFile()).toBe(true);expect(fs.readdirSync(path.join(dir,'.mutation-lock-leases'))).toEqual([]);expect(fs.readdirSync(dir).some(name=>name.includes('.legacy-'))).toBe(false);});
test('an expired timestamp cannot steal a lock from a live owner',()=>{const dir=tmp(),lock=path.join(dir,'.mutation-lock');fs.mkdirSync(lock);fs.writeFileSync(path.join(lock,'owner.json'),JSON.stringify({pid:process.pid,token:'live',createdAt:0,expiresAt:0}));expect(()=>withLock(dir,()=>42)).toThrow('Another helper');});
test('an incomplete legacy lock is never age-reclaimed from a paused initializer',()=>{const dir=tmp(),lock=path.join(dir,'.mutation-lock'),entered=path.join(dir,'entered');fs.mkdirSync(lock);const old=new Date(Date.now()-60_000);fs.utimesSync(lock,old,old);expect(()=>withLock(dir,()=>fs.writeFileSync(entered,'unsafe'))).toThrow('may still be initializing');expect(fs.existsSync(entered)).toBe(false);expect(fs.lstatSync(lock).isDirectory()).toBe(true);fs.writeFileSync(path.join(lock,'owner.json'),JSON.stringify({pid:process.pid,token:'paused',createdAt:0,expiresAt:0}));expect(()=>withLock(dir,()=>fs.writeFileSync(entered,'unsafe'))).toThrow('Another helper');expect(fs.existsSync(entered)).toBe(false);});
test('a dead immutable lease is recovered without replacing its inode',()=>{const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='a'.repeat(32),lease=path.join(leases,`${token}.json`);fs.writeFileSync(lease,JSON.stringify({pid:2147483647,token,createdAt:0})+'\n',{mode:0o600,flag:'wx'});expect(withLock(dir,()=>2)).toBe(2);expect(fs.existsSync(lease)).toBe(false);});
test('an in-flight cooperating lease publication is contention rather than poisoned state',()=>{const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='9'.repeat(32),lease=path.join(leases,`${token}.json`),temporary=`${lease}.tmp.${process.pid}.deadbeef`,write=fs.writeFileSync,lstat=fs.lstatSync;let injected=false,transitioned=false,entered=false,caught:unknown;const writer=spyOn(fs,'writeFileSync').mockImplementation(((file:any,data:any,options:any)=>{const result=write(file,data,options);const candidate=String(file);if(!injected&&path.dirname(candidate)===leases&&/^[a-f0-9]{32}\.json\.tmp\.\d+\.[a-f0-9]{8}$/.test(path.basename(candidate))){injected=true;write(temporary,JSON.stringify({pid:process.pid,token,createdAt:Date.now()})+'\n',{mode:0o600,flag:'wx'});}return result;}) as typeof fs.writeFileSync),reader=spyOn(fs,'lstatSync').mockImplementation(((file:any,options?:any)=>{if(!transitioned&&String(file)===temporary){transitioned=true;fs.linkSync(temporary,lease);fs.unlinkSync(temporary);}return options===undefined?lstat(file):lstat(file,options);}) as typeof fs.lstatSync);try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{reader.mockRestore();writer.mockRestore();}expect(injected).toBe(true);expect(transitioned).toBe(true);expect(entered).toBe(false);expect(caught).toMatchObject({code:'INSUFFICIENT_CAPACITY'});expect(fs.readdirSync(leases)).toEqual([path.basename(lease)]);});
test('a live publisher unlinking its temp after target observation remains ordinary contention',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);
const leases=path.join(dir,'.mutation-lock-leases'),token='7'.repeat(32),lease=path.join(leases,`${token}.json`),temporary=`${lease}.tmp.${process.pid}.abcdef12`;
fs.writeFileSync(temporary,JSON.stringify({pid:process.pid,token,createdAt:Date.now()})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(temporary,lease);
const lstat=fs.lstatSync;let targetReads=0,transitioned=false,entered=false,caught:unknown;
const reader=spyOn(fs,'lstatSync').mockImplementation(((file:any,options?:any)=>{
if(String(file)===lease&&++targetReads===2){fs.unlinkSync(temporary);transitioned=true;}
return options===undefined?lstat(file):lstat(file,options);
}) as typeof fs.lstatSync);
try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{reader.mockRestore();}
expect(transitioned).toBe(true);expect(entered).toBe(false);expect(caught).toMatchObject({code:'INSUFFICIENT_CAPACITY'});expect(fs.statSync(lease).nlink).toBe(1);
});
test('a live publisher unlinking its temp after canonical validation is rescanned as contention',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);
const leases=path.join(dir,'.mutation-lock-leases'),token='5'.repeat(32),lease=path.join(leases,`${token}.json`),temporary=`${lease}.tmp.${process.pid}.a1b2c3d4`;
fs.writeFileSync(temporary,JSON.stringify({pid:process.pid,token,createdAt:Date.now()})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(temporary,lease);
const readdir=fs.readdirSync;let leaseReads=0,transitioned=false,entered=false,caught:unknown;
const reader=spyOn(fs,'readdirSync').mockImplementation(((directory:any,options?:any)=>{
if(String(directory)===leases&&++leaseReads===2){fs.unlinkSync(temporary);transitioned=true;}
return options===undefined?readdir(directory):readdir(directory,options);
}) as typeof fs.readdirSync);
try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{reader.mockRestore();}
expect(transitioned).toBe(true);expect(entered).toBe(false);expect(caught).toMatchObject({code:'INSUFFICIENT_CAPACITY'});expect(fs.statSync(lease).nlink).toBe(1);
});
test('a live temp linked after the absent-target check remains ordinary contention',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);
const leases=path.join(dir,'.mutation-lock-leases'),token='8'.repeat(32),lease=path.join(leases,`${token}.json`),temporary=`${lease}.tmp.${process.pid}.1234abcd`;
fs.writeFileSync(temporary,JSON.stringify({pid:process.pid,token,createdAt:Date.now()})+'\n',{mode:0o600,flag:'wx'});
const exists=fs.existsSync;let transitioned=false,entered=false,caught:unknown;
const observer=spyOn(fs,'existsSync').mockImplementation(((file:any)=>{
const result=exists(file);if(!transitioned&&String(file)===lease&&!result){fs.linkSync(temporary,lease);transitioned=true;}return result;
}) as typeof fs.existsSync);
try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{observer.mockRestore();}
expect(transitioned).toBe(true);expect(entered).toBe(false);expect(caught).toMatchObject({code:'INSUFFICIENT_CAPACITY'});expect(fs.statSync(lease).nlink).toBe(2);
});
test('an unrecognized multiply-linked lease temp remains poisoned state',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);
const leases=path.join(dir,'.mutation-lock-leases'),token='6'.repeat(32),temporary=path.join(leases,`${token}.json.tmp.2147483647.1234abcd`),outside=path.join(dir,'outside-link');
fs.writeFileSync(temporary,JSON.stringify({pid:2147483647,token,createdAt:0})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(temporary,outside);
let entered=false,caught:unknown;try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}
expect(entered).toBe(false);expect(caught).toMatchObject({code:'UNSAFE_PATH'});expect(fs.existsSync(temporary)).toBe(true);expect(fs.existsSync(outside)).toBe(true);
});
test('a same-inode same-size content rewrite is not treated as publication progress',()=>{
const dir=tmp(),token='0'.repeat(32),publisherPid=2147483647,target=path.join(dir,`${token}.json`),temporary=`${target}.tmp.${publisherPid}.abcdef12`,original=JSON.stringify({pid:publisherPid,token,createdAt:0})+'\n',replacement=JSON.stringify({pid:publisherPid,token,createdAt:1})+'\n';expect(replacement.length).toBe(original.length);fs.writeFileSync(temporary,original,{mode:0o600,flag:'wx'});fs.linkSync(temporary,target);
const fstat=fs.fstatSync;let reads=0,rewritten=false;const reader=spyOn(fs,'fstatSync').mockImplementation(((fd:any,options?:any)=>{if(++reads===2){fs.writeFileSync(target,replacement);rewritten=true;}return options===undefined?fstat(fd):fstat(fd,options);}) as typeof fs.fstatSync);
let caught:unknown;try{discardAtomicNoReplaceTemp(temporary,publisherPid,{label:'Test publication',maxBytes:4096});}catch(error){caught=error;}finally{reader.mockRestore();}
expect(rewritten).toBe(true);expect(caught).toMatchObject({code:'SNAPSHOT_RACE'});expect(fs.existsSync(target)).toBe(true);expect(fs.existsSync(temporary)).toBe(true);
});
test('a live publisher identity cannot launder a stable same-size rewrite as contention',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='f'.repeat(32),publisherPid=process.pid,target=path.join(leases,`${token}.json`),temporary=`${target}.tmp.${publisherPid}.abcdef12`,original=JSON.stringify({pid:publisherPid,token,createdAt:0})+'\n',replacement=JSON.stringify({pid:publisherPid,token,createdAt:1})+'\n';expect(replacement.length).toBe(original.length);fs.writeFileSync(temporary,original,{mode:0o600,flag:'wx'});fs.linkSync(temporary,target);
const fstat=fs.fstatSync;let reads=0,rewritten=false,entered=false,caught:unknown;const reader=spyOn(fs,'fstatSync').mockImplementation(((fd:any,options?:any)=>{if(++reads===2){fs.writeFileSync(target,replacement);rewritten=true;}return options===undefined?fstat(fd):fstat(fd,options);}) as typeof fs.fstatSync);try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{reader.mockRestore();}
expect(rewritten).toBe(true);expect(entered).toBe(false);expect(caught).toMatchObject({code:'SNAPSHOT_RACE'});expect(fs.existsSync(target)).toBe(true);expect(fs.existsSync(temporary)).toBe(true);
});
test('a stable malformed hard-link publication stays unsafe even when its named PID is live',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='9'.repeat(32),target=path.join(leases,`${token}.json`),temporary=`${target}.tmp.${process.pid}.deadbeef`;fs.writeFileSync(temporary,'{"malformed":\n',{mode:0o600,flag:'wx'});fs.linkSync(temporary,target);let entered=false,caught:unknown;try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}expect(entered).toBe(false);expect(caught).toMatchObject({code:'UNSAFE_PATH'});expect(fs.existsSync(target)).toBe(true);expect(fs.existsSync(temporary)).toBe(true);
});
test('a zero-byte hard-linked publication stays unsafe even when its named PID is live',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='7'.repeat(32),target=path.join(leases,`${token}.json`),temporary=`${target}.tmp.${process.pid}.deadbeef`;fs.writeFileSync(temporary,'',{mode:0o600,flag:'wx'});fs.linkSync(temporary,target);let entered=false,caught:unknown;try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}expect(entered).toBe(false);expect(caught).toMatchObject({code:'UNSAFE_PATH'});expect(fs.statSync(target).nlink).toBe(2);expect(fs.statSync(temporary).nlink).toBe(2);
});
test('settled publication contention cannot launder a stable incoherent lease phase',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),poisonToken='a'.repeat(32),liveToken='b'.repeat(32),poison=path.join(leases,`${poisonToken}.active.0000000000000001`),temporary=path.join(leases,`${liveToken}.json.tmp.${process.pid}.abcdef12`);fs.writeFileSync(poison,JSON.stringify({pid:process.pid,token:poisonToken,createdAt:0})+'\n',{mode:0o600,flag:'wx'});
const write=fs.writeFileSync;let injected=false;const publisher=spyOn(fs,'writeFileSync').mockImplementation(((file:any,data:any,options:any)=>{const result=write(file,data,options),name=path.basename(String(file));if(!injected&&path.dirname(String(file))===leases&&/^[a-f0-9]{32}\.json\.tmp\.\d+\.[a-f0-9]{8}$/.test(name)&&!name.startsWith(liveToken)){injected=true;write(temporary,JSON.stringify({pid:process.pid,token:liveToken,createdAt:0})+'\n',{mode:0o600,flag:'wx'});}return result;}) as typeof fs.writeFileSync);
const wait=Atomics.wait.bind(Atomics);let settled=false,entered=false,caught:unknown;const sleeper=spyOn(Atomics,'wait').mockImplementation(((array:any,index:any,value:any,timeout?:any)=>{if(!settled&&fs.existsSync(temporary)){settled=true;fs.unlinkSync(temporary);}return wait(array,index,value,timeout);}) as typeof Atomics.wait);try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{sleeper.mockRestore();publisher.mockRestore();}
expect(injected).toBe(true);expect(settled).toBe(true);expect(entered).toBe(false);expect(caught).toMatchObject({code:'UNSAFE_PATH'});expect(fs.existsSync(poison)).toBe(true);
});
test('successful recovery on the final bounded rescan remains transient capacity',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),liveToken='c'.repeat(32),temporary=path.join(leases,`${liveToken}.json.tmp.${process.pid}.abcdef12`),write=fs.writeFileSync;let injected=false;const publisher=spyOn(fs,'writeFileSync').mockImplementation(((file:any,data:any,options:any)=>{const result=write(file,data,options),name=path.basename(String(file));if(!injected&&path.dirname(String(file))===leases&&/^[a-f0-9]{32}\.json\.tmp\.\d+\.[a-f0-9]{8}$/.test(name)&&!name.startsWith(liveToken)){injected=true;write(temporary,JSON.stringify({pid:process.pid,token:liveToken,createdAt:0})+'\n',{mode:0o600,flag:'wx'});}return result;}) as typeof fs.writeFileSync);
const now=spyOn(Date,'now').mockReturnValue(1000),kill=process.kill.bind(process);let probes=0,entered=false,caught:unknown;const liveness=spyOn(process,'kill').mockImplementation(((pid:any,signal?:any)=>{if(Number(pid)===process.pid&&signal===0&&++probes===532){const error:any=new Error('gone');error.code='ESRCH';throw error;}return Number(pid)===process.pid&&signal===0?true:(signal===undefined?kill(pid):kill(pid,signal));}) as typeof process.kill);try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{liveness.mockRestore();now.mockRestore();publisher.mockRestore();}
expect(injected).toBe(true);expect(probes).toBe(532);expect(entered).toBe(false);expect(caught).toMatchObject({code:'INSUFFICIENT_CAPACITY'});expect(fs.existsSync(temporary)).toBe(false);expect(fs.readdirSync(leases)).toEqual([]);
});
test('a settled publication remains bound to the originally observed inode',()=>{
const dir=tmp(),token='4'.repeat(32),publisherPid=2147483647,target=path.join(dir,`${token}.json`),temporary=`${target}.tmp.${publisherPid}.abcdef12`,parked=`${target}.replaced`,owner=JSON.stringify({pid:publisherPid,token,createdAt:0})+'\n';
fs.writeFileSync(temporary,owner,{mode:0o600,flag:'wx'});fs.linkSync(temporary,target);const original=fs.statSync(target).ino,lstat=fs.lstatSync;let targetReads=0,replaced=false,caught:unknown;
const reader=spyOn(fs,'lstatSync').mockImplementation(((file:any,options?:any)=>{
if(String(file)===target&&++targetReads===2){fs.unlinkSync(target);fs.renameSync(temporary,parked);fs.writeFileSync(target,owner,{mode:0o600,flag:'wx'});replaced=true;}
return options===undefined?lstat(file):lstat(file,options);
}) as typeof fs.lstatSync);
try{discardAtomicNoReplaceTemp(temporary,publisherPid,{label:'Test publication',maxBytes:4096,validate:(value,pid)=>{expect((value as any).token).toBe(token);expect(pid).toBe(publisherPid);}});}catch(error){caught=error;}finally{reader.mockRestore();}
expect(replaced).toBe(true);expect(caught).toMatchObject({code:'UNSAFE_PATH'});expect(fs.statSync(parked).ino).toBe(original);expect(fs.statSync(target).ino).not.toBe(original);
});
test('a link transition on an open inode cannot launder a replacement pathname',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);
const leases=path.join(dir,'.mutation-lock-leases'),token='3'.repeat(32),publisherPid=2147483647,target=path.join(leases,`${token}.json`),temporary=`${target}.tmp.${publisherPid}.1234abcd`,owner=JSON.stringify({pid:publisherPid,token,createdAt:0})+'\n';
fs.writeFileSync(temporary,owner,{mode:0o600,flag:'wx'});fs.linkSync(temporary,target);const original=fs.statSync(target).ino,fstat=fs.fstatSync;let reads=0,replaced=false,entered=false,caught:unknown;
const reader=spyOn(fs,'fstatSync').mockImplementation(((fd:any,options?:any)=>{
if(++reads===2){fs.unlinkSync(target);fs.writeFileSync(target,owner,{mode:0o600,flag:'wx'});replaced=true;}
return options===undefined?fstat(fd):fstat(fd,options);
}) as typeof fs.fstatSync);
try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{reader.mockRestore();}
expect(replaced).toBe(true);expect(entered).toBe(false);expect(caught).toMatchObject({name:'CsoError',code:'SNAPSHOT_RACE'});expect(fs.statSync(target).ino).not.toBe(original);expect(fs.existsSync(temporary)).toBe(true);
});
test('a cooperating lease released between open and fstat is rescanned',()=>{
const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);
const leases=path.join(dir,'.mutation-lock-leases'),token='2'.repeat(32),lease=path.join(leases,`${token}.json`);fs.writeFileSync(lease,JSON.stringify({pid:process.pid,token,createdAt:Date.now()})+'\n',{mode:0o600,flag:'wx'});
const fstat=fs.fstatSync;let released=false,entered=false;const reader=spyOn(fs,'fstatSync').mockImplementation(((fd:any,options?:any)=>{if(!released){fs.unlinkSync(lease);released=true;}return options===undefined?fstat(fd):fstat(fd,options);}) as typeof fs.fstatSync);
try{withLock(dir,()=>{entered=true;});}finally{reader.mockRestore();}
expect(released).toBe(true);expect(entered).toBe(true);expect(fs.existsSync(lease)).toBe(false);
});
test('recovers the exact no-replace lease hard link left by a dead publisher',()=>{const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='1'.repeat(32),lease=path.join(leases,`${token}.json`),temporary=`${lease}.tmp.2147483647.deadbeef`;fs.writeFileSync(lease,JSON.stringify({pid:2147483647,token,createdAt:0})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(lease,temporary);expect(fs.statSync(lease).nlink).toBe(2);expect(withLock(dir,()=>2)).toBe(2);expect(fs.existsSync(lease)).toBe(false);expect(fs.existsSync(temporary)).toBe(false);});
test('private immutable reads recover only a recognized same-inode no-replace temp',()=>{const dir=tmp(),artifact=path.join(dir,'artifact.json'),temporary=`${artifact}.tmp.2147483647.cafebabe`;fs.writeFileSync(artifact,JSON.stringify({value:'bound'})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(artifact,temporary);expect(readJson(artifact)).toEqual({value:'bound'});expect(fs.statSync(artifact).nlink).toBe(1);expect(fs.existsSync(temporary)).toBe(false);const poisoned=path.join(dir,'poisoned.json'),unrecognized=`${poisoned}.tmp.dead.bad`;fs.writeFileSync(poisoned,'{}\n',{mode:0o600,flag:'wx'});fs.linkSync(poisoned,unrecognized);expect(()=>readJson(poisoned)).toThrow('hard link does not match');expect(fs.existsSync(unrecognized)).toBe(true);});
test('private JSON reads reject rename and symlink substitutions between validation and open',()=>{for(const kind of ['rename','symlink'] as const){const dir=tmp(),artifact=path.join(dir,'artifact.json'),original=path.join(dir,'original.json'),poison=path.join(dir,'poison.json');fs.writeFileSync(artifact,JSON.stringify({value:'bound'})+'\n',{mode:0o600});fs.writeFileSync(poison,JSON.stringify({value:'poison'})+'\n',{mode:0o600});const open=fs.openSync,patched=spyOn(fs,'openSync').mockImplementation(((candidate:any,flags:any,mode?:any)=>{if(String(candidate)===artifact){fs.renameSync(artifact,original);if(kind==='rename')fs.renameSync(poison,artifact);else fs.symlinkSync(poison,artifact);}return mode===undefined?open(candidate,flags):open(candidate,flags,mode);}) as typeof fs.openSync);try{expect(()=>readJson(artifact)).toThrow(/changed while it was opened/);}finally{patched.mockRestore();}}});
test('mismatched lease publisher identity fails closed',()=>{const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='2'.repeat(32),lease=path.join(leases,`${token}.json`),temporary=`${lease}.tmp.2147483646.abcdef12`;fs.writeFileSync(lease,JSON.stringify({pid:2147483647,token,createdAt:0})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(lease,temporary);expect(()=>withLock(dir,()=>2)).toThrow('does not match its publisher');expect(fs.statSync(lease).nlink).toBe(2);});
test('malformed and multiply-linked immutable owners fail closed instead of being reclaimed',()=>{for(const kind of ['schema','hardlink'] as const){const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token=(kind==='schema'?'c':'d').repeat(32),lease=path.join(leases,`${token}.json`),value=JSON.stringify(kind==='schema'?{pid:'dead',token,createdAt:0}:{pid:2147483647,token,createdAt:0})+'\n';fs.writeFileSync(lease,value,{mode:0o600,flag:'wx'});if(kind==='hardlink')fs.linkSync(lease,path.join(dir,'outside-link'));let entered=false;expect(()=>withLock(dir,()=>{entered=true;})).toThrow('Run mutation lease');expect(entered).toBe(false);expect(fs.existsSync(lease)).toBe(true);}});
test('PID reuse is distinguished by the Linux process-start identity',()=>{if(process.platform!=='linux')return;const raw=fs.readFileSync(`/proc/${process.pid}/stat`,'utf8'),tail=raw.slice(raw.lastIndexOf(')')+2).trim().split(/\s+/),identity=BigInt(tail[19]),dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='e'.repeat(32),lease=path.join(leases,`${token}.json`);fs.writeFileSync(lease,JSON.stringify({pid:process.pid,processIdentity:`linux:${identity+1n}`,token,createdAt:0})+'\n',{mode:0o600,flag:'wx'});expect(withLock(dir,()=>2)).toBe(2);expect(fs.existsSync(lease)).toBe(false);});
test('interrupted lease publications use process identity to distinguish PID reuse',()=>{if(process.platform!=='linux')return;const raw=fs.readFileSync(`/proc/${process.pid}/stat`,'utf8'),tail=raw.slice(raw.lastIndexOf(')')+2).trim().split(/\s+/),identity=BigInt(tail[19]);for(const linked of [false,true]){const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token=(linked?'b':'a').repeat(32),lease=path.join(leases,`${token}.json`),temporary=`${lease}.tmp.${process.pid}.abcdef12`,owner=JSON.stringify({pid:process.pid,processIdentity:`linux:${identity+1n}`,token,createdAt:0})+'\n';fs.writeFileSync(temporary,owner,{mode:0o600,flag:'wx'});if(linked)fs.linkSync(temporary,lease);expect(withLock(dir,()=>2)).toBe(2);expect(fs.existsSync(temporary)).toBe(false);expect(fs.existsSync(lease)).toBe(false);}});
test('EPERM liveness keeps an active cross-process lease intact',()=>{const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),token='d'.repeat(32),pid=2147483647,createdAt=0,candidate=path.join(leases,`${token}.json`),decision=path.join(leases,`${token}.decision`),ticket='1'.padStart(16,'0'),active=path.join(leases,`${token}.active.${ticket}`);fs.writeFileSync(candidate,JSON.stringify({pid,token,createdAt})+'\n',{mode:0o600,flag:'wx'});const stat=fs.statSync(candidate);fs.writeFileSync(decision,JSON.stringify({schemaVersion:1,token,kind:'ticket',ticket,candidateDev:String(stat.dev),candidateIno:String(stat.ino),ownerPid:pid,ownerCreatedAt:createdAt,publisherPid:pid,createdAt})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(candidate,active);const kill=process.kill.bind(process),probe=spyOn(process,'kill').mockImplementation(((target:any,signal?:any)=>{if(Number(target)===pid){const error:any=new Error('not permitted');error.code='EPERM';throw error;}return signal===undefined?kill(target):kill(target,signal);}) as typeof process.kill);let entered=false,caught:unknown;try{withLock(dir,()=>{entered=true;});}catch(error){caught=error;}finally{probe.mockRestore();}expect(entered).toBe(false);expect(caught).toMatchObject({code:'INSUFFICIENT_CAPACITY'});expect([candidate,decision,active].every(file=>fs.existsSync(file))).toBe(true);expect(fs.statSync(candidate).nlink).toBe(2);expect(fs.statSync(decision).nlink).toBe(1);});
test('an active lease binds a separate decision to its candidate and active inode',()=>{const dir=tmp();withLock(dir,()=>{const leases=path.join(dir,'.mutation-lock-leases'),names=fs.readdirSync(leases);expect(names.length).toBe(3);const candidate=names.find(name=>name.endsWith('.json'))!,decision=names.find(name=>name.endsWith('.decision'))!,active=names.find(name=>name.includes('.active.'))!,candidateStat=fs.statSync(path.join(leases,candidate)),decisionStat=fs.statSync(path.join(leases,decision)),activeStat=fs.statSync(path.join(leases,active)),record=JSON.parse(fs.readFileSync(path.join(leases,decision),'utf8'));expect(`${candidateStat.dev}:${candidateStat.ino}`).toBe(`${activeStat.dev}:${activeStat.ino}`);expect(candidateStat.nlink).toBe(2);expect(activeStat.nlink).toBe(2);expect(`${decisionStat.dev}:${decisionStat.ino}`).not.toBe(`${candidateStat.dev}:${candidateStat.ino}`);expect(decisionStat.nlink).toBe(1);expect(record.kind).toBe('ticket');expect(record.candidateDev).toBe(String(candidateStat.dev));expect(record.candidateIno).toBe(String(candidateStat.ino));});expect(fs.readdirSync(path.join(dir,'.mutation-lock-leases'))).toEqual([]);});
test('post-link verification failure cleans every recorded lease phase',()=>{const dir=tmp();expect(withLock(dir,()=>1)).toBe(1);const leases=path.join(dir,'.mutation-lock-leases'),fakeToken='f'.repeat(32),fakeTemp=path.join(leases,`${fakeToken}.json.tmp.${process.pid}.deadbeef`),link=fs.linkSync,injectedOwner=JSON.stringify({pid:process.pid,token:fakeToken,createdAt:Date.now()})+'\n';let injected=false,caught:unknown;const publisher=spyOn(fs,'linkSync').mockImplementation(((source:any,target:any)=>{const result=link(source,target);if(!injected&&String(target).includes('.active.')){injected=true;fs.writeFileSync(fakeTemp,injectedOwner,{mode:0o600,flag:'wx'});}return result;}) as typeof fs.linkSync);try{withLock(dir,()=>1);}catch(error){caught=error;}finally{publisher.mockRestore();}expect(injected).toBe(true);expect(caught).toMatchObject({code:'INSUFFICIENT_CAPACITY'});expect(fs.readdirSync(leases)).toEqual([path.basename(fakeTemp)]);fs.unlinkSync(fakeTemp);expect(withLock(dir,()=>2)).toBe(2);expect(fs.readdirSync(leases)).toEqual([]);});
test('an async callback retains its lease until settlement without blocking its own event loop',async()=>{const dir=tmp();let release!:()=>void;const pending=new Promise<void>(resolve=>{release=resolve;}),outer=withLock(dir,async()=>{await pending;return 7;}),started=Date.now();expect(()=>withLock(dir,()=>99)).toThrow('Another operation in this helper');expect(Date.now()-started).toBeLessThan(200);release();expect(await outer).toBe(7);expect(withLock(dir,()=>9)).toBe(9);});
test('a synchronous exact-release failure is attempted only once',()=>{const dir=tmp(),lstat=fs.lstatSync;let observed=0,reader:any;try{expect(()=>withLock(dir,()=>{const leases=path.join(dir,'.mutation-lock-leases'),names=fs.readdirSync(leases),candidate=path.join(leases,names.find(name=>name.endsWith('.json'))!),active=path.join(leases,names.find(name=>name.includes('.active.'))!),value=fs.readFileSync(active);fs.unlinkSync(active);fs.writeFileSync(active,value,{mode:0o600,flag:'wx'});reader=spyOn(fs,'lstatSync').mockImplementation(((file:any,options?:any)=>{if(String(file)===candidate)observed++;return options===undefined?lstat(file):lstat(file,options);}) as typeof fs.lstatSync);})).toThrow('active phase changed before cleanup');expect(observed).toBe(1);}finally{reader?.mockRestore();}});
test('exact release rejects active or decision inode substitution and overrides callback success or failure',()=>{for(const phase of ['active','decision'] as const){const dir=tmp();let replacement='',parked='';expect(()=>withLock(dir,()=>{const leases=path.join(dir,'.mutation-lock-leases'),name=fs.readdirSync(leases).find(value=>phase==='active'?value.includes('.active.'):value.endsWith('.decision'))!,lease=path.join(leases,name),value=fs.readFileSync(lease);parked=`${lease}.replaced`;fs.renameSync(lease,parked);fs.writeFileSync(lease,value,{mode:0o600,flag:'wx'});replacement=lease;if(phase==='active')throw new Error('callback failed');return 1;})).toThrow(/changed before (cleanup|exact release)/);expect(fs.existsSync(replacement)).toBe(true);expect(fs.existsSync(parked)).toBe(true);expect(fs.statSync(replacement).ino).not.toBe(fs.statSync(parked).ino);fs.rmSync(dir,{recursive:true,force:true});}});
test('concurrent stale-lock recovery never admits overlapping report writers',async()=>{const dir=tmp(),lock=path.join(dir,'.mutation-lock'),script=path.join(dir,'racer.ts');fs.mkdirSync(lock);fs.writeFileSync(path.join(lock,'owner.json'),JSON.stringify({pid:2147483647,token:'stale',createdAt:0,expiresAt:0}));fs.writeFileSync(script,`import fs from 'node:fs';import path from 'node:path';import {withLock} from ${JSON.stringify(path.resolve(import.meta.dir,'../lib/cso/state.ts'))};const dir=process.env.RACE_DIR!;try{await withLock(dir,async()=>{const active=path.join(dir,'active');try{fs.writeFileSync(active,String(process.pid),{flag:'wx'});}catch{fs.appendFileSync(path.join(dir,'overlap'),'yes\\n');}await Bun.sleep(50);try{if(fs.readFileSync(active,'utf8')===String(process.pid))fs.unlinkSync(active);}catch{}});}catch{}`);const children=Array.from({length:8},()=>spawn(process.execPath,[script],{env:{...process.env,RACE_DIR:dir},stdio:'ignore'}));await Promise.all(children.map(child=>new Promise<void>(resolve=>child.on('close',()=>resolve()))));expect(fs.existsSync(path.join(dir,'overlap'))).toBe(false);});
test('concurrent recovery of one dead hard-link publication has a winner and no raw race errors',async()=>{
const dir=tmp(),barrier=path.join(dir,'barrier'),script=path.join(dir,'recovery-racer.ts');fs.mkdirSync(barrier);expect(withLock(dir,()=>1)).toBe(1);
const leases=path.join(dir,'.mutation-lock-leases'),token='c'.repeat(32),lease=path.join(leases,`${token}.json`),temporary=`${lease}.tmp.2147483647.deadbeef`;
fs.writeFileSync(temporary,JSON.stringify({pid:2147483647,token,createdAt:0})+'\n',{mode:0o600,flag:'wx'});fs.linkSync(temporary,lease);
fs.writeFileSync(script,`import fs from 'node:fs';import path from 'node:path';import {withLock} from ${JSON.stringify(path.resolve(import.meta.dir,'../lib/cso/state.ts'))};const dir=process.env.RACE_DIR!,barrier=process.env.BARRIER!,id=process.env.RACE_ID!,wait=new Int32Array(new SharedArrayBuffer(4));fs.writeFileSync(path.join(barrier,'ready-'+id),'ready',{flag:'wx'});while(!fs.existsSync(path.join(barrier,'go')))Atomics.wait(wait,0,0,1);let outcome='success';try{withLock(dir,()=>{const active=path.join(barrier,'work-active');try{fs.writeFileSync(active,id,{flag:'wx'});}catch{fs.appendFileSync(path.join(barrier,'overlap'),'yes\\n');}Atomics.wait(wait,0,0,20);try{if(fs.readFileSync(active,'utf8')===id)fs.unlinkSync(active);}catch{}});}catch(error){outcome=String(error?.code??error)+':'+String(error?.message??'');}fs.writeFileSync(path.join(barrier,'result-'+id),outcome);`);
const children=Array.from({length:8},(_,index)=>spawn(process.execPath,[script],{env:{...process.env,RACE_DIR:dir,BARRIER:barrier,RACE_ID:String(index)},stdio:'ignore'})),done=children.map(child=>childExit(child));
try{
for(let index=0;index<children.length;index++)await waitForPath(path.join(barrier,`ready-${index}`));fs.writeFileSync(path.join(barrier,'go'),'go');await Promise.all(done);
const outcomes=children.map((_,index)=>fs.readFileSync(path.join(barrier,`result-${index}`),'utf8'));
expect(outcomes).toContain('success');expect(outcomes.filter(value=>value!=='success'&&!value.startsWith('INSUFFICIENT_CAPACITY:'))).toEqual([]);
expect(fs.existsSync(path.join(barrier,'overlap'))).toBe(false);expect(fs.readdirSync(leases)).toEqual([]);
}finally{stopChildren(children);}
});
test('two synchronized cooperating publishers admit work without overlap or an all-loser outcome',async()=>{
const dir=tmp(),barrier=path.join(dir,'barrier'),script=path.join(dir,'contender.ts');fs.mkdirSync(barrier);expect(withLock(dir,()=>1)).toBe(1);
fs.writeFileSync(script,`import fs from 'node:fs';import path from 'node:path';import {withLock} from ${JSON.stringify(path.resolve(import.meta.dir,'../lib/cso/state.ts'))};
const dir=process.env.RACE_DIR!,barrier=process.env.BARRIER!,id=process.env.RACE_ID!,leases=path.join(dir,'.mutation-lock-leases'),read=fs.readdirSync;let reads=0;
fs.readdirSync=((candidate,...args)=>{if(String(candidate)===leases&&++reads===2){fs.writeFileSync(path.join(barrier,'ready-'+id),'ready',{flag:'wx'});const deadline=Date.now()+5000;while(fs.readdirSync(barrier).filter(name=>name.startsWith('ready-')).length<2){if(Date.now()>deadline)throw new Error('barrier timeout');Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,1);}}return read.call(fs,candidate,...args);});
let outcome='success';try{withLock(dir,()=>{const active=path.join(barrier,'active');try{fs.writeFileSync(active,id,{flag:'wx'});}catch{fs.writeFileSync(path.join(barrier,'overlap'),'overlap');}Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,25);try{if(fs.readFileSync(active,'utf8')===id)fs.unlinkSync(active);}catch{}});}catch(error){outcome=String(error?.code??error);}fs.writeFileSync(path.join(barrier,'result-'+id),outcome);`);
const children=['a','b'].map(id=>spawn(process.execPath,[script],{env:{...process.env,RACE_DIR:dir,BARRIER:barrier,RACE_ID:id},stdio:'ignore'}));
await Promise.all(children.map(child=>new Promise<void>((resolve,reject)=>child.on('error',reject).on('close',code=>code===0?resolve():reject(new Error(`contender exited ${code}`))))));
const outcomes=['a','b'].map(id=>fs.readFileSync(path.join(barrier,`result-${id}`),'utf8'));
expect(outcomes.filter(value=>value==='success').length).toBeGreaterThan(0);expect(outcomes.every(value=>value==='success'||value==='INSUFFICIENT_CAPACITY')).toBe(true);expect(fs.existsSync(path.join(barrier,'overlap'))).toBe(false);
});
test('equal ticket decisions use token order and a late withdrawal cannot revoke the active winner',async()=>{
if(process.platform==='win32')return;
const dir=tmp(),barrier=path.join(dir,'barrier'),script=path.join(dir,'contender.ts'),lowToken='1'.repeat(32),highToken='e'.repeat(32);fs.mkdirSync(barrier);expect(withLock(dir,()=>1)).toBe(1);fs.writeFileSync(script,leaseContenderSource());
const env={...process.env,RACE_DIR:dir,BARRIER:barrier},children:Array<ReturnType<typeof spawn>>=[];
try{
const low=spawn(process.execPath,[script],{env:{...env,RACE_ID:'L',RACE_MODE:'pause-before-decision',RACE_TOKEN:lowToken,RACE_HOLD:'1'},stdio:'ignore'});children.push(low);const lowDone=childExit(low);await waitForPath(path.join(barrier,'paused-L'));expect(fs.readFileSync(path.join(barrier,'choice-L'),'utf8')).toBe('0000000000000001');
const high=spawn(process.execPath,[script],{env:{...env,RACE_ID:'H',RACE_MODE:'pause-before-withdraw',RACE_TOKEN:highToken},stdio:'ignore'});children.push(high);const highDone=childExit(high);await waitForPath(path.join(barrier,'paused-H'),2500);expect(fs.readFileSync(path.join(barrier,'choice-H'),'utf8')).toBe('0000000000000001');
process.kill(low.pid!,'SIGCONT');await waitForPath(path.join(barrier,'entered-L'));const decision=JSON.parse(fs.readFileSync(path.join(dir,'.mutation-lock-leases',lowToken+'.decision'),'utf8'));expect(decision.kind).toBe('ticket');expect(decision.ticket).toBe('0000000000000001');
process.kill(high.pid!,'SIGCONT');await waitForPath(path.join(barrier,'result-H'));expect(fs.readFileSync(path.join(barrier,'result-H'),'utf8')).toBe('INSUFFICIENT_CAPACITY');expect(await highDone).toBe(0);
fs.writeFileSync(path.join(barrier,'release-L'),'release');await waitForPath(path.join(barrier,'result-L'));expect(fs.readFileSync(path.join(barrier,'result-L'),'utf8')).toBe('success');expect(await lowDone).toBe(0);expect(fs.existsSync(path.join(barrier,'overlap'))).toBe(false);expect(fs.readdirSync(path.join(dir,'.mutation-lock-leases'))).toEqual([]);
}finally{stopChildren(children);}
});
test('a paused candidate is fenced so a progressing ticket wins within the bound',async()=>{if(process.platform==='win32')return;const dir=tmp(),barrier=path.join(dir,'barrier'),script=path.join(dir,'contender.ts');fs.mkdirSync(barrier);expect(withLock(dir,()=>1)).toBe(1);fs.writeFileSync(script,leaseContenderSource());const env={...process.env,RACE_DIR:dir,BARRIER:barrier},children:Array<ReturnType<typeof spawn>>=[];try{const earlier=spawn(process.execPath,[script],{env:{...env,RACE_ID:'L',RACE_MODE:'pause-candidate'},stdio:'ignore'});children.push(earlier);const earlierDone=childExit(earlier);await waitForPath(path.join(barrier,'paused-L'));const started=Date.now(),later=spawn(process.execPath,[script],{env:{...env,RACE_ID:'H',RACE_MODE:'normal'},stdio:'ignore'});children.push(later);const laterDone=childExit(later);await waitForPath(path.join(barrier,'result-H'),2500);expect(Date.now()-started).toBeLessThan(2000);expect(fs.readFileSync(path.join(barrier,'result-H'),'utf8')).toBe('success');expect(await laterDone).toBe(0);process.kill(earlier.pid!,'SIGCONT');await waitForPath(path.join(barrier,'result-L'));expect(fs.readFileSync(path.join(barrier,'result-L'),'utf8')).toBe('INSUFFICIENT_CAPACITY');expect(await earlierDone).toBe(0);expect(fs.existsSync(path.join(barrier,'overlap'))).toBe(false);expect(fs.readdirSync(path.join(dir,'.mutation-lock-leases'))).toEqual([]);}finally{stopChildren(children);}});
test('a paused earlier ticket bounds later contenders without withdrawing the winner',async()=>{if(process.platform==='win32')return;const dir=tmp(),barrier=path.join(dir,'barrier'),script=path.join(dir,'contender.ts');fs.mkdirSync(barrier);expect(withLock(dir,()=>1)).toBe(1);fs.writeFileSync(script,leaseContenderSource());const env={...process.env,RACE_DIR:dir,BARRIER:barrier},children:Array<ReturnType<typeof spawn>>=[];try{const earlier=spawn(process.execPath,[script],{env:{...env,RACE_ID:'L',RACE_MODE:'pause-ticket'},stdio:'ignore'});children.push(earlier);const earlierDone=childExit(earlier);await waitForPath(path.join(barrier,'paused-L'));const started=Date.now(),later=spawn(process.execPath,[script],{env:{...env,RACE_ID:'H',RACE_MODE:'normal'},stdio:'ignore'});children.push(later);const laterDone=childExit(later);await waitForPath(path.join(barrier,'result-H'),2500);expect(Date.now()-started).toBeLessThan(2000);expect(fs.readFileSync(path.join(barrier,'result-H'),'utf8')).toBe('INSUFFICIENT_CAPACITY');expect(await laterDone).toBe(0);process.kill(earlier.pid!,'SIGCONT');await waitForPath(path.join(barrier,'result-L'));expect(fs.readFileSync(path.join(barrier,'result-L'),'utf8')).toBe('success');expect(await earlierDone).toBe(0);expect(fs.existsSync(path.join(barrier,'overlap'))).toBe(false);expect(fs.readdirSync(path.join(dir,'.mutation-lock-leases'))).toEqual([]);}finally{stopChildren(children);}});
test('a paused loser cleanup leaves only an ineligible decision and cannot block the winner',async()=>{if(process.platform==='win32')return;const dir=tmp(),barrier=path.join(dir,'barrier'),script=path.join(dir,'contender.ts');fs.mkdirSync(barrier);expect(withLock(dir,()=>1)).toBe(1);fs.writeFileSync(script,leaseContenderSource());const env={...process.env,RACE_DIR:dir,BARRIER:barrier},children:Array<ReturnType<typeof spawn>>=[];try{const winner=spawn(process.execPath,[script],{env:{...env,RACE_ID:'L',RACE_MODE:'pause-ticket'},stdio:'ignore'});children.push(winner);const winnerDone=childExit(winner);await waitForPath(path.join(barrier,'paused-L'));const loser=spawn(process.execPath,[script],{env:{...env,RACE_ID:'H',RACE_MODE:'pause-withdraw-cleanup'},stdio:'ignore'});children.push(loser);const loserDone=childExit(loser);await waitForPath(path.join(barrier,'paused-H'));const phases=fs.readdirSync(path.join(dir,'.mutation-lock-leases'));expect(phases.filter(name=>name.endsWith('.decision')).length).toBe(2);expect(phases.filter(name=>name.endsWith('.json')).length).toBe(1);process.kill(winner.pid!,'SIGCONT');await waitForPath(path.join(barrier,'result-L'));expect(fs.readFileSync(path.join(barrier,'result-L'),'utf8')).toBe('success');expect(await winnerDone).toBe(0);process.kill(loser.pid!,'SIGCONT');await waitForPath(path.join(barrier,'result-H'));expect(fs.readFileSync(path.join(barrier,'result-H'),'utf8')).toBe('INSUFFICIENT_CAPACITY');expect(await loserDone).toBe(0);expect(fs.existsSync(path.join(barrier,'overlap'))).toBe(false);expect(fs.readdirSync(path.join(dir,'.mutation-lock-leases'))).toEqual([]);}finally{stopChildren(children);}});
test('a paused later contender cannot turn a cached lower ticket into an all-loser outcome',async()=>{if(process.platform==='win32')return;const dir=tmp(),barrier=path.join(dir,'barrier'),script=path.join(dir,'contender.ts');fs.mkdirSync(barrier);expect(withLock(dir,()=>1)).toBe(1);fs.writeFileSync(script,leaseContenderSource());const env={...process.env,RACE_DIR:dir,BARRIER:barrier},children:Array<ReturnType<typeof spawn>>=[];try{const earlier=spawn(process.execPath,[script],{env:{...env,RACE_ID:'L',RACE_MODE:'wait-active',RACE_HOLD:'1'},stdio:'ignore'});children.push(earlier);const earlierDone=childExit(earlier);await waitForPath(path.join(barrier,'doorway-L'));const later=spawn(process.execPath,[script],{env:{...env,RACE_ID:'H',RACE_MODE:'pause-later-scan'},stdio:'ignore'});children.push(later);const laterDone=childExit(later);await waitForPath(path.join(barrier,'paused-H'));await waitForPath(path.join(barrier,'entered-L'));process.kill(later.pid!,'SIGCONT');await waitForPath(path.join(barrier,'result-H'),2500);expect(fs.readFileSync(path.join(barrier,'result-H'),'utf8')).toBe('INSUFFICIENT_CAPACITY');expect(await laterDone).toBe(0);fs.writeFileSync(path.join(barrier,'release-L'),'release');await waitForPath(path.join(barrier,'result-L'));expect(fs.readFileSync(path.join(barrier,'result-L'),'utf8')).toBe('success');expect(await earlierDone).toBe(0);expect(fs.existsSync(path.join(barrier,'overlap'))).toBe(false);expect(fs.readdirSync(path.join(dir,'.mutation-lock-leases'))).toEqual([]);}finally{stopChildren(children);}});
test('legacy report imports expire after thirty days without following links',()=>{const old=process.env.GSTACK_HOME,base=tmp();process.env.GSTACK_HOME=base;try{const archive=secureDirectory(path.join(base,'security','cso','legacy-imports')),expired=path.join(archive,'a'.repeat(64)+'.json'),retained=path.join(archive,'b'.repeat(64)+'.json');fs.writeFileSync(expired,'{}');fs.writeFileSync(retained,'{}');const now=Date.now();fs.utimesSync(expired,new Date(now-31*86400_000),new Date(now-31*86400_000));retention(now);expect(fs.existsSync(expired)).toBe(false);expect(fs.existsSync(retained)).toBe(true);const unsafe=path.join(archive,'c'.repeat(64)+'.json');fs.symlinkSync(retained,unsafe);expect(()=>retention(now)).toThrow('unsafe artifact');expect(fs.readFileSync(retained,'utf8')).toBe('{}');}finally{if(old===undefined)delete process.env.GSTACK_HOME;else process.env.GSTACK_HOME=old;}});
test('an active recheck pins only its expired parent report, never expired repair material',()=>{const now=Date.now(),repo='f'.repeat(24),oldRun=`${now-31*86400_000}-${'a'.repeat(16)}`,childRun=`${now}-${'b'.repeat(16)}`,findingId='c'.repeat(32),repoDir=secureDirectory(path.join(privateRoot(),repo)),parent=secureDirectory(path.join(repoDir,oldRun)),child=secureDirectory(path.join(repoDir,childRun)),bundles=secureDirectory(path.join(parent,'bundles')),reviews=secureDirectory(path.join(parent,'reviews')),attempts=secureDirectory(path.join(parent,'verification-attempts'));writeHelperJson(path.join(parent,'report.json'),{schemaVersion:3,runId:oldRun,repoId:repo,status:'finished',coverage:[],findings:[{id:findingId}]});writeJson(path.join(bundles,'bundle.json'),{schemaVersion:3});writeJson(path.join(reviews,'review.json'),{schemaVersion:3});writeJson(path.join(attempts,'attempt.json'),{schemaVersion:3});writeHelperJson(path.join(child,'report.json'),{schemaVersion:3,runId:childRun,repoId:repo,status:'running',deadline:new Date(now+60_000).toISOString(),coverage:[],findings:[],parent:{runId:oldRun,findingId,kind:'recheck'}});retention(now);expect(fs.existsSync(path.join(parent,'report.json'))).toBe(true);expect(fs.existsSync(bundles)).toBe(false);expect(fs.existsSync(reviews)).toBe(false);expect(fs.existsSync(attempts)).toBe(false);fs.rmSync(child,{recursive:true});retention(now);expect(fs.existsSync(parent)).toBe(false);});
test('finished, expired, and malformed child reports cannot extend parent retention',()=>{const now=Date.now(),repo='e'.repeat(24),repoDir=secureDirectory(path.join(privateRoot(),repo)),findingId='d'.repeat(32);for(const [index,childValue] of [[0,{status:'finished',repoId:repo,deadline:new Date(now+60_000).toISOString()}],[1,{status:'running',repoId:repo,deadline:new Date(now-1).toISOString()}],[2,{status:'running',repoId:'0'.repeat(24),deadline:new Date(now+60_000).toISOString()}]] as const){const parentRun=`${now-(31+index)*86400_000}-${String(index+1).repeat(16)}`,childRun=`${now-index}-${String(index+4).repeat(16)}`,parent=secureDirectory(path.join(repoDir,parentRun)),child=secureDirectory(path.join(repoDir,childRun));writeHelperJson(path.join(parent,'report.json'),{schemaVersion:3,runId:parentRun,repoId:repo,status:'finished',coverage:[],findings:[{id:findingId}]});writeHelperJson(path.join(child,'report.json'),{schemaVersion:3,runId:childRun,repoId:childValue.repoId,status:childValue.status,deadline:childValue.deadline,coverage:[],findings:[],parent:{runId:parentRun,findingId,kind:'recheck'}});}retention(now);for(const name of fs.readdirSync(repoDir).filter(name=>Number(name.split('-')[0])<now-30*86400_000))expect(fs.existsSync(path.join(repoDir,name))).toBe(false);});
});
+40 -36
View File
@@ -1,44 +1,48 @@
/**
* Cross-skill taxonomy alignment. The canonical taxonomy lives in
* lib/redact-patterns.ts (single source of truth). /spec and /cso both reference
* it by pointer rather than inlining the full catalog (size discipline). This
* test guards that the recognizable HIGH-tier prefixes stay present in /cso's
* archaeology prose. (A fourth test covered the resolver-generated taxonomy
* table; that generator was deleted as dead code no template ever used it.)
*/
import { describe, test, expect } from "bun:test";
import * as fs from "fs";
import * as path from "path";
/** CSO uses versioned domain mappings and the shared fail-closed secret taxonomy. */
import { describe, test, expect } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
const ROOT = path.resolve(import.meta.dir, "..");
// cso is carved (skeleton + sections/audit-phases.md). The Secrets Archaeology
// prose + secret prefixes moved into the section; check the union so relocated
// content still counts.
function unionSkill(skill: string): string {
let t = fs.readFileSync(path.join(ROOT, skill, "SKILL.md"), "utf-8");
const dir = path.join(ROOT, skill, "sections");
if (fs.existsSync(dir)) {
for (const f of fs.readdirSync(dir).sort()) {
if (f.endsWith(".md") && !f.endsWith(".md.tmpl")) t += "\n" + fs.readFileSync(path.join(dir, f), "utf-8");
}
}
return t;
}
const CSO = unionSkill("cso");
const ROOT = path.resolve(import.meta.dir, '..');
const CSO = fs.readFileSync(path.join(ROOT, 'cso/SKILL.md'), 'utf8') + '\n'
+ fs.readFileSync(path.join(ROOT, 'cso/sections/audit-phases.md'), 'utf8');
describe("cso/spec taxonomy alignment", () => {
test("cso archaeology names the recognizable HIGH-tier prefixes", () => {
for (const s of ["AKIA", "ghp_", "sk-ant-", "BEGIN"]) {
expect(CSO).toContain(s);
}
describe('CSO domain source and redaction contracts', () => {
test('credential recognition uses the shared taxonomy without raw history commands', () => {
for (const prefix of ['AKIA', 'ghp_', 'sk-ant-', 'BEGIN']) expect(CSO).toContain(prefix);
expect(CSO).toContain('lib/redact-patterns.ts');
expect(CSO).toContain('Secrets Archaeology');
expect(CSO).toContain('Never print raw `git log -p --all`');
expect(CSO).toContain('Do not call live provider APIs');
});
test("cso points to lib/redact-patterns.ts as the single source of truth", () => {
expect(CSO).toContain("lib/redact-patterns.ts");
test('OWASP 2025 mapping does not retain obsolete 2021 category numbers', () => {
const rows = CSO.split('\n').filter(line => /^\| A\d\d \|/.test(line));
expect(rows).toHaveLength(10);
const categories = new Map(rows.map(line => {
const [, id, name] = line.split('|').map(part => part.trim());
return [id, name];
}));
expect(categories.get('A02')).toBe('Security Misconfiguration');
expect(categories.get('A03')).toBe('Software Supply Chain Failures');
expect(categories.get('A10')).toBe('Mishandling of Exceptional Conditions');
expect(rows.find(line => line.startsWith('| A01 |'))).toContain('SSRF');
});
test("cso keeps its git-history archaeology (different use case, not replaced)", () => {
expect(CSO).toContain("git log -p --all");
expect(CSO).toContain("Secrets Archaeology");
test('domain standards carry inspected versions and no inferred compliance', () => {
for (const version of ['OWASP Top 10:2025', 'OWASP API Security Top 10:2023', 'ASVS version: 5.0.0', 'v5.0.0-1.2.5', 'LLM Top 10 2026', 'Agentic Applications Top 10 2026', 'MCP security guidance version: 2026-07-28']) expect(CSO).toContain(version);
expect(CSO).toContain('artifact 56857');
expect(CSO).toContain('unset publication-date field');
expect(CSO).toContain('artifact 52117');
expect(CSO).toContain('Do not invent IDs');
});
test('all declared scanners retain execution and evidence boundaries', () => {
for (const scanner of ['Gitleaks', 'OSV-Scanner', 'Semgrep', 'zizmor', 'Trivy', 'Schemathesis']) expect(CSO).toContain(scanner);
expect(CSO).toContain('Import existing SARIF');
expect(CSO).toContain('public wheels');
expect(CSO).toContain('Gemfile.lock` parsed as inert data');
expect(CSO).toContain('Python `--no-build` alone');
expect(CSO).toContain('every database connection');
});
});
+205
View File
@@ -0,0 +1,205 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { PublicArchiveCache } from '../lib/cso/cache';
import type { VerificationRequest } from '../lib/cso/contracts';
import { dockerEndpoint, type DockerEndpoint } from '../lib/cso/docker';
import { DockerPreparationSandboxRunner } from '../lib/cso/preparation-docker';
import {
admitPreparationRuntime, admitPreparationSidecar, PreparationExecutor,
type PreparedApplication, type PreparationRuntimeAdmission, type RailsDatabaseSelection,
} from '../lib/cso/preparation-executor';
import { inspectPreparation, type CsoStack, type PreparationPlan } from '../lib/cso/preparation';
import { CSO_HELPER_ABI, type QualifiedRuntime, type RuntimeCatalog } from '../lib/cso/runtime-catalog';
import { completeRuntimeCatalogFixture } from './helpers/cso-runtime-catalog';
import { secureDirectory } from '../lib/cso/state';
import { canonicalStartPlan, canonicalTestPlan, DockerVerificationExecutor } from '../lib/cso/verification';
const requestedStack = process.env.GSTACK_CSO_TEST_STACK;
const requested = process.env.GSTACK_CSO_DOCKER_TESTS === '1' && ['bun', 'python', 'rails'].includes(requestedStack ?? '');
const suite = requested ? describe : describe.skip;
const platform = process.arch === 'arm64' ? 'linux/arm64' : 'linux/amd64';
let root = '', watchdog = '', endpoint: DockerEndpoint, runtime: QualifiedRuntime, catalog: RuntimeCatalog;
function qualification(kind: 'application' | 'postgresql') {
const common = { sourceCommit: 'b'.repeat(40), workflow: 'https://github.com/garrytan/gstack/actions/runs/1',
sbomDigest: `sha256:${'b'.repeat(64)}`, provenanceDigest: `sha256:${'c'.repeat(64)}`, verifiedProvenance: true as const };
return kind === 'application'
? { ...common, kind, containmentPassed: true as const, coldStartPassed: true as const, positiveNegativeAssertionsPassed: true as const, heldOutRepairPassed: true as const }
: { ...common, kind, containmentPassed: true as const, coldStartPassed: true as const, multiDatabasePassed: true as const, readinessPassed: true as const };
}
function qualified(stack: CsoStack | 'postgresql', image: string, versions: Record<string, string>): QualifiedRuntime {
return { id: `${stack}-staged-cold-${process.arch}`, stack, platform, state: 'qualified', image,
entrypoint: '/opt/cso/entrypoint', helperAbi: CSO_HELPER_ABI, versions, policyVersion: 'cso-isolation-v1',
qualifiedAt: '2026-09-09T00:00:00.000Z', qualification: qualification(stack === 'postgresql' ? 'postgresql' : 'application') };
}
function writeFiles(directory: string, files: Record<string, string>): void {
for (const [relative, body] of Object.entries(files)) {
const file = path.join(directory, relative); fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
fs.writeFileSync(file, body, { mode: 0o600 });
}
}
function bunSource(directory: string): void {
writeFiles(directory, {
'package.json': JSON.stringify({ name: 'cso-bun-cold', version: '1.0.0', private: true,
scripts: { start: 'bun app.js', test: 'bun test' }, dependencies: { 'escape-html': '1.0.3' } }) + '\n',
'bun.lock': `{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": { "": { "name": "cso-bun-cold", "dependencies": { "escape-html": "1.0.3" } } },
"packages": { "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="] }
}\n`,
'app.js': `import escape from "escape-html";
const port=Number(process.env.PORT);Bun.serve({hostname:"127.0.0.1",port,fetch(request){const route=new URL(request.url).pathname;if(route==="/control")return new Response("CONTROL_OK:"+escape("<dependency>"));if(route==="/security")return new Response("DENIED",{status:403});return new Response("missing",{status:404})}});
`,
'app.test.js': `import {expect,test} from "bun:test";import escape from "escape-html";test("cold dependency is executable",()=>expect(escape("<dependency>")).toBe("&lt;dependency&gt;"));\n`,
});
}
function pythonSource(directory: string): void {
writeFiles(directory, {
'requirements.txt': `Django==4.2.30 --hash=sha256:4d07aaf1c62f9984842b67c2874ebbf7056a17be253860299b93ae1881faad65
asgiref==3.11.1 --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133
sqlparse==0.5.5 --hash=sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba
typing_extensions==4.16.0 --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8
`,
'manage.py': `#!/usr/bin/env python3
import os,sys
os.environ.setdefault("DJANGO_SETTINGS_MODULE","settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
`,
'settings.py': `SECRET_KEY="cso-cold-fixture"\nDEBUG=False\nALLOWED_HOSTS=["127.0.0.1"]\nROOT_URLCONF="urls"\nINSTALLED_APPS=[]\nMIDDLEWARE=[]\n`,
'urls.py': `from django.http import HttpResponse
from django.urls import path
def control(_request): return HttpResponse("CONTROL_OK:Django")
def security(_request): return HttpResponse("DENIED",status=403)
urlpatterns=[path("control",control),path("security",security)]
`,
'test_app.py': `import os,unittest
os.environ.setdefault("DJANGO_SETTINGS_MODULE","settings")
import django
django.setup()
from django.test import Client
class ColdStartTest(unittest.TestCase):
def test_dependency_and_control(self): self.assertContains(Client().get("/control"),"CONTROL_OK",status_code=200)
`,
});
}
function railsSource(directory: string, bundlerVersion: string): void {
let lock = fs.readFileSync(path.join(import.meta.dir, 'fixtures/cso-eval/rails.Gemfile.lock'), 'utf8');
lock = lock.replace(' pp (0.6.4)\n', ' pg (1.6.2)\n pp (0.6.4)\n')
.replace(' puma (= 7.2.0)\n', ' pg (= 1.6.2)\n puma (= 7.2.0)\n')
.replace(/(BUNDLED WITH\n)\s+[^\n]+/, `$1 ${bundlerVersion}`);
writeFiles(directory, {
'Gemfile': `source "https://rubygems.org"\ngem "rails", "= 8.1.2"\ngem "puma", "= 7.2.0"\ngem "sqlite3", "= 2.9.0"\ngem "pg", "= 1.6.2"\n`,
'Gemfile.lock': lock,
'config/boot.rb': `ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)\nrequire "bundler/setup"\n`,
'config/application.rb': `require_relative "boot"\nrequire "rails"\nrequire "active_record/railtie"\nrequire "action_controller/railtie"\nmodule CsoCold;class Application < Rails::Application;config.load_defaults 8.1;config.eager_load=false;config.hosts.clear;end;end\n`,
'config/environment.rb': `require_relative "application"\nRails.application.initialize!\n`,
'config/routes.rb': `Rails.application.routes.draw do\n get "/control",to:"probe#control"\n get "/security",to:"probe#security"\nend\n`,
'config/environments/test.rb': `Rails.application.configure do\n config.eager_load=false\n config.consider_all_requests_local=true\nend\n`,
'config/database.yml': `default: &default\n pool: 3\ntest:\n primary:\n <<: *default\n adapter: sqlite3\n database: storage/test.sqlite3\n queue:\n <<: *default\n adapter: postgresql\n database: cso_queue\n`,
'app/controllers/application_controller.rb': `class ApplicationController < ActionController::Base;end\n`,
'app/controllers/probe_controller.rb': `class ProbeController < ApplicationController\n def control;render plain:"CONTROL_OK:Rails";end\n def security;render plain:"DENIED",status: :forbidden;end\nend\n`,
'Rakefile': `require_relative "config/application"\nRails.application.load_tasks\n`,
'config.ru': `require_relative "config/environment"\nrun Rails.application\n`,
'db/schema.rb': `ActiveRecord::Schema[8.1].define(version: 1) do\n create_table :cold_records, force: true do |t|\n t.string :name\n end\nend\n`,
'test/test_helper.rb': `ENV["RAILS_ENV"] ||= "test"\nrequire_relative "../config/environment"\nrequire "rails/test_help"\n`,
'test/cold_start_test.rb': `require "test_helper"\nclass ColdStartTest < ActionDispatch::IntegrationTest\n test "control boots" do\n get "/control"\n assert_response :success\n assert_includes response.body,"CONTROL_OK"\n end\nend\n`,
});
}
function readyPlan(source: string, stack: CsoStack): PreparationPlan {
const plan = inspectPreparation(source, stack);
if (plan.status !== 'ready') throw new Error(`${stack} cold fixture is not ready: ${plan.prerequisites.map(item => `${item.code}: ${item.message}`).join('; ')}`);
return plan;
}
function preparation(runDir: string, admission: PreparationRuntimeAdmission): PreparationExecutor {
const staging = secureDirectory(path.join(runDir, 'archive-staging'));
const cache = new PublicArchiveCache({ root: path.join(runDir, 'public-cache'), stagingRoot: staging, maxBytes: 512 * 1024 * 1024 });
const runner = new DockerPreparationSandboxRunner({ endpoint, watchdogPath: watchdog, runRoot: runDir,
controlRoot: secureDirectory(path.join(runDir, 'preparation-execution')), admission });
return new PreparationExecutor({ cache, runner, materializationRoot: secureDirectory(path.join(runDir, 'archive-materializations')) });
}
function requestFor(source: string, stack: CsoStack, port: number): VerificationRequest {
const start = canonicalStartPlan(source, stack, port), tests = canonicalTestPlan(source, stack);
return { findingId: 'd'.repeat(32), runtimeProfile: runtime.id, port, start: start.command,
legitimate: [{ name: 'cold-start control', path: '/control', method: 'GET', expected: { status: 200, includes: 'CONTROL_OK' } }],
security: { name: 'fixture denial', path: '/security', method: 'GET', expected: { status: 403, includes: 'DENIED' }, vulnerable: { status: 200, includes: 'SECRET' } },
existingTests: tests.commands, fixtures: {}, boundaryFiles: start.entrypointFiles, testFiles: tests.files, changes: [],
review: { reviewer: 'cold-start-gate', independent: true, rootCauseRepaired: true, featurePreserved: true,
boundaryMocks: false, rationale: 'Cold-start execution only.', reviewedPatchHash: 'e'.repeat(64) } };
}
async function executeCold(stack: CsoStack, source: string, runDir: string, port: number, database?: RailsDatabaseSelection): Promise<PreparedApplication> {
const plan = readyPlan(source, stack), admission = admitPreparationRuntime({ plan, platform, profile: runtime.id, catalog });
const prep = preparation(runDir, admission), deadline = Date.now() + 15 * 60_000;
const closure = await prep.acquire({ plan, admission, snapshot: source, deadline });
expect(closure.archives.length).toBeGreaterThan(0);
expect(closure.archives.every(item => item.requestedUrl.startsWith('https://'))).toBe(true);
if (stack === 'bun') expect(closure.archives.every(item => item.resolvedUrl?.startsWith('https://'))).toBe(true);
else expect(closure.archives.every(item => item.resolvedUrl === null)).toBe(true);
const prepared = await prep.prepareOffline({ plan, admission, snapshot: source, closure, deadline, database });
try {
const observation = await new DockerVerificationExecutor(endpoint, watchdog, deadline).observe(
prepared.preparedRoot, 'after', requestFor(prepared.preparedRoot, stack, port), runtime, runtime,
secureDirectory(path.join(runDir, 'observations')), secureDirectory(path.join(runDir, 'verification-controls')),
{ environment: prepared.executionEnvironment, database: prepared.database },
);
expect(observation).toMatchObject({ booted: true, legitimate: true, security: 'pass', existingTests: true });
return prepared;
} finally { await prep.dispose(prepared); }
}
beforeAll(async () => {
if (!requested) return;
const image = process.env.GSTACK_CSO_TEST_IMAGE;
if (!image) throw new Error(`${requestedStack} cold-start qualification requires GSTACK_CSO_TEST_IMAGE; a requested gate cannot skip`);
if (process.env.GSTACK_CSO_TEST_PLATFORM !== platform) throw new Error(`${requestedStack} cold-start qualification requires native ${platform}`);
const versions = JSON.parse(process.env.GSTACK_CSO_EXPECTED_VERSIONS || '{}') as Record<string, string>;
for (const key of ({ bun: ['bun','cso-preparation'], python: ['python','uv','cso-preparation'], rails: ['ruby','bundler','cso-preparation'] } as const)[requestedStack as 'bun'|'python'|'rails'])
if (!/^\d+\.\d+\.\d+$/.test(versions[key] ?? '')) throw new Error(`${requestedStack} cold-start qualification requires exact ${key} metadata`);
root = fs.mkdtempSync(path.join(os.tmpdir(), `cso-${requestedStack}-cold-`)); process.env.GSTACK_HOME = path.join(root, 'state');
watchdog = path.resolve(import.meta.dir, '../bin/gstack-cso-watchdog');
if (!fs.existsSync(watchdog)) throw new Error(`${requestedStack} cold-start qualification requires the compiled CSO watchdog`);
endpoint = await dockerEndpoint(secureDirectory(path.join(root, 'docker-home')), { HOME: root, DOCKER_HOST: process.env.DOCKER_HOST ?? 'unix:///var/run/docker.sock' });
catalog = completeRuntimeCatalogFixture(`${requestedStack}-staged-cold`);
const installRuntime = (value: QualifiedRuntime): QualifiedRuntime => {
const runtimeIndex = catalog.runtimes.findIndex(item => item.stack === value.stack && item.platform === value.platform);
const profile = catalog.profiles.find(item => item.stack === value.stack && item.platform === value.platform)!;
const installed = { ...value, id: profile.id };
catalog.runtimes[runtimeIndex] = installed;
profile.versions = { ...installed.versions };
return installed;
};
runtime = installRuntime(qualified(requestedStack as CsoStack, image, versions));
if (requestedStack === 'rails') {
const postgresImage = process.env.GSTACK_CSO_TEST_POSTGRES_IMAGE;
const postgresVersion = process.env.GSTACK_CSO_TEST_POSTGRES_VERSION;
if (!postgresImage || !/^\d+\.\d+(?:\.\d+)?$/.test(postgresVersion ?? '')) throw new Error('Rails cold-start qualification requires a staged PostgreSQL digest and exact version');
installRuntime(qualified('postgresql', postgresImage, { postgresql: postgresVersion! }));
}
});
afterAll(() => { if (root) fs.rmSync(root, { recursive: true, force: true }); delete process.env.GSTACK_HOME; });
suite('CSO staged Bun, Python, and Rails cold-start journeys', () => {
test('acquires, prepares, boots, controls, and tests the requested staged stack with no target egress', async () => {
const source = secureDirectory(path.join(root, 'source'));
if (requestedStack === 'bun') bunSource(source);
else if (requestedStack === 'python') pythonSource(source);
else railsSource(source, runtime.versions.bundler);
if (requestedStack !== 'rails') {
const prepared = await executeCold(requestedStack as CsoStack, source, secureDirectory(path.join(root, 'run')), requestedStack === 'bun' ? 34610 : 34611);
expect(prepared.database).toBeUndefined();
return;
}
const plan = readyPlan(source, 'rails');
expect(plan.database).toMatchObject({ supported: ['sqlite', 'postgresql'], connections: ['primary', 'queue'] });
const appAdmission = admitPreparationRuntime({ plan, platform, profile: runtime.id, catalog });
const sidecar = admitPreparationSidecar({ platform, catalog });
const sqlite = await executeCold('rails', source, secureDirectory(path.join(root, 'run-sqlite')), 34612, { adapter: 'sqlite' });
expect(sqlite.database).toEqual({ adapter: 'sqlite', connections: ['primary', 'queue'] });
const postgres = await executeCold('rails', source, secureDirectory(path.join(root, 'run-postgresql')), 34613, { adapter: 'postgresql', sidecar });
expect(postgres.database).toMatchObject({ adapter: 'postgresql', connections: ['primary', 'queue'], sidecar: { id: sidecar.runtime.id } });
expect(appAdmission.catalogRevision).toBe(sidecar.catalogRevision);
}, 25 * 60_000);
});
+41
View File
@@ -0,0 +1,41 @@
import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { sha256 } from '../lib/cso/contracts';
import { patchHash, treeHash, verifyRepair } from '../lib/cso/verification';
const roots:string[]=[];
afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
describe('CSO verification persistence ordering',()=>{
test('cleanup acknowledgement failure leaves no certified bundle on disk',async()=>{
const runDir=fs.mkdtempSync(path.join(os.tmpdir(),'cso-cleanup-order-'));roots.push(runDir);
const snapshot=path.join(runDir,'snapshot');fs.mkdirSync(snapshot);
const files:Record<string,string>={
'package.json':JSON.stringify({scripts:{test:'node --test'}}),
'app.js':'vulnerable\n',
'app.test.js':'test("ok",()=>{})\n',
};
for(const [name,body] of Object.entries(files))fs.writeFileSync(path.join(snapshot,name),body);
const request:any={findingId:'a'.repeat(32),runtimeProfile:'node',port:3456,start:{executable:'/usr/local/bin/node',args:['app.js']},
legitimate:[{name:'health',path:'/health',method:'GET',expected:{status:200,includes:'ok'}}],
security:{name:'tenant isolation',path:'/user?id=2',method:'GET',expected:{status:403},vulnerable:{status:200,includes:'tenant-b'}},
existingTests:[{executable:'/usr/local/bin/node',args:['--test','--test-reporter=tap','./app.test.js']}],testFiles:['app.test.js'],fixtures:{},boundaryFiles:['app.js'],
changes:[{path:'app.js',beforeSha256:sha256(files['app.js']),after:'fixed\n',effect:'source'}],
review:{reviewer:'independent-reviewer',independent:true,rootCauseRepaired:true,featurePreserved:true,boundaryMocks:false,
rationale:'The tenant predicate is restored without changing the feature',reviewedPatchHash:''}};
request.review.reviewedPatchHash=patchHash(request);
const entries=Object.entries(files).map(([name,body])=>({path:name,originalHash:sha256(body),executionHash:sha256(body),bytes:Buffer.byteLength(body),mode:0o600}));
const manifest:any={version:3,createdAt:'2026-01-01T00:00:00Z',expiresAt:'2026-01-08T00:00:00Z',root:'/repo',headCommit:'b'.repeat(40),
originalHash:'c'.repeat(64),executionHash:treeHash(snapshot),entries};
const watchdog=path.join(runDir,'no-ack-watchdog');
fs.writeFileSync(watchdog,'#!/bin/sh\nset -eu\ntouch "$6/attempt.ready"\nwhile test ! -f "$6/attempt.terminal"; do sleep 0.01; done\nexit 0\n',{mode:0o755});
const runtime:any={id:'node',stack:'node',image:`runtime@sha256:${'d'.repeat(64)}`,platform:'linux/amd64'};
const executor:any={observe:async(_source:string,phase:'before'|'after')=>({booted:true,legitimate:true,
security:phase==='before'?'intended_failure':'pass',existingTests:true,output:'ok',inputHash:''})};
await expect(verifyRepair({runId:'run',runDir,manifest,rawRequest:request,runtime,verifier:runtime,policyHash:'e'.repeat(64),archives:[],executor,
watchdogPath:watchdog,attemptDeadline:Date.now()+10_000})).rejects.toThrow('did not acknowledge');
const bundles=path.join(runDir,'bundles');expect(fs.existsSync(bundles)?fs.readdirSync(bundles):[]).toEqual([]);
});
});
+28
View File
@@ -0,0 +1,28 @@
import { describe, expect, test } from 'bun:test';
import { boundedResponseBody } from '../lib/cso/verifier';
describe('CSO bounded loopback verifier response reader',()=>{
test('accepts a response exactly at the configured byte limit',async()=>{
const body='x'.repeat(65_536);
expect(await boundedResponseBody(new Response(body))).toBe(body);
});
test('cancels an endless chunked response immediately after the byte limit',async()=>{
let pulls=0,cancelled=false;
const stream=new ReadableStream<Uint8Array>({
pull(controller){pulls++;controller.enqueue(new Uint8Array(8192));},
cancel(){cancelled=true;},
});
await expect(boundedResponseBody(new Response(stream))).rejects.toThrow('response too large');
expect(cancelled).toBe(true);
expect(pulls).toBeLessThanOrEqual(10);
});
test('rejects an oversized declared body before consuming it',async()=>{
let cancelled=false;
const stream=new ReadableStream<Uint8Array>({pull(controller){controller.enqueue(new Uint8Array(1));},cancel(){cancelled=true;}});
const response=new Response(stream,{headers:{'content-length':'65537'}});
await expect(boundedResponseBody(response)).rejects.toThrow('response too large');
expect(cancelled).toBe(true);
});
});
+17
View File
@@ -0,0 +1,17 @@
import { afterEach, describe, expect, test } from 'bun:test';import * as fs from 'node:fs';import * as os from 'node:os';import * as path from 'node:path';import { spawn, spawnSync } from 'node:child_process';
import { supervisePreparedCall } from '../lib/cso/preparation-docker';
const dirs:string[]=[];const tmp=()=>{const p=fs.mkdtempSync(path.join(os.tmpdir(),'csowatchdog-'));dirs.push(p);return p;};afterEach(()=>{for(const p of dirs.splice(0))fs.rmSync(p,{recursive:true,force:true});});
function compile(dir:string){const out=path.join(dir,'watchdog'),r=spawnSync('/usr/bin/cc',['-std=c11','-D_POSIX_C_SOURCE=200809L','-O2','-Wall','-Wextra',path.resolve(import.meta.dir,'../lib/cso/watchdog.c'),'-o',out],{encoding:'utf8',timeout:30_000});expect(r.status).toBe(0);expect(r.stderr).toBe('');return out;}
function pinnedEndpoint(dir:string){const socket=path.join(dir,'daemon.sock'),made=spawnSync('/usr/bin/python3',['-c','import socket,sys; s=socket.socket(socket.AF_UNIX); s.bind(sys.argv[1]); s.close()',socket],{encoding:'utf8'});expect(made.status).toBe(0);const stat=fs.lstatSync(socket);expect(stat.isSocket()).toBe(true);return{uri:`unix://${socket}`,device:stat.dev,inode:stat.ino};}
describe('detached CSO watchdog',()=>{
test('prepared-call guard acknowledges normal exact cleanup',async()=>{const run=tmp(),watchdog=compile(run),call=path.join(run,'preparation-execution','offline-call'),control=path.join(run,'supervision','prepared-call');fs.mkdirSync(call,{recursive:true});fs.mkdirSync(control,{recursive:true});fs.writeFileSync(path.join(call,'prepared-source'),'copy');const guard=await supervisePreparedCall({watchdogPath:watchdog,ownerPid:process.pid,deadline:Date.now()+10_000,runRoot:run,callRoot:call,controlRoot:control});await guard.dispose();expect(fs.existsSync(call)).toBe(false);expect(fs.existsSync(control)).toBe(false);});
test('prepared-call guard removes retained output after owner death and deadline',async()=>{for(const mode of ['owner','deadline'] as const){const run=tmp(),watchdog=compile(run),call=path.join(run,'preparation-execution',mode),control=path.join(run,'supervision',mode),event=path.join(control,'attempt.event');fs.mkdirSync(call,{recursive:true});fs.mkdirSync(control,{recursive:true});fs.writeFileSync(path.join(call,'prepared-source'),'copy');const owner=mode==='owner'?spawn('/bin/sleep',['30'],{stdio:'ignore'}):undefined,guard=await supervisePreparedCall({watchdogPath:watchdog,ownerPid:owner?.pid??process.pid,deadline:Date.now()+(mode==='owner'?10_000:50),runRoot:run,callRoot:call,controlRoot:control});if(owner)owner.kill('SIGKILL');for(let attempt=0;attempt<40&&(!fs.existsSync(event)||fs.existsSync(call));attempt++)await Bun.sleep(100);expect(fs.existsSync(call)).toBe(false);expect(fs.readFileSync(event,'utf8')).toContain(`${mode==='owner'?'supervisor-death':'deadline'} execution-copy cleanup complete`);await guard.dispose();expect(fs.existsSync(control)).toBe(false);}});
test('survives supervisor death and cleans journaled plus label-race resources',async()=>{const dir=tmp(),watchdog=compile(dir),endpoint=pinnedEndpoint(dir),log=path.join(dir,'docker.log'),docker=path.join(dir,'docker'),id='a'.repeat(64),race='e'.repeat(64),lease=path.join(dir,'lease'),token='c'.repeat(32);fs.mkdirSync(lease);fs.writeFileSync(path.join(lease,'lease.json'),'{}');fs.writeFileSync(path.join(lease,'lease.token'),token+'\n');fs.writeFileSync(docker,`#!/bin/sh\nif [ "$3" = ps ]; then if [ "$8" = "id=${id}" ]; then printf '%s\\n' '${id}'; else printf '%s\\n' '${race}'; fi; exit 0; fi\nprintf '%s\\n' "$*" >> '${log}'\n`,{mode:0o755});fs.writeFileSync(path.join(dir,'resources.journal'),`container:${id}\n`,{mode:0o600});const owner=spawn('/bin/sleep',['1'],{stdio:'ignore'}),child=spawn(watchdog,['--owner',String(owner.pid),'--deadline',String(Math.ceil((Date.now()+10_000)/1000)),'--run-dir',dir,'--docker',docker,'--endpoint',endpoint.uri,'--socket-device',String(endpoint.device),'--socket-inode',String(endpoint.inode),'--run-label','test-label','--lease-path',lease,'--lease-token',token],{stdio:'ignore'}),closed=new Promise<number|null>(resolve=>child.on('close',resolve));expect(await closed).toBe(0);const calls=fs.readFileSync(log,'utf8');expect(calls).toContain(`rm --force --volumes ${id}`);expect(calls).toContain(`rm --force --volumes ${race}`);expect(fs.readFileSync(path.join(dir,'watchdog.event'),'utf8')).toContain('supervisor-death cleanup complete');expect(fs.existsSync(lease)).toBe(false);});
test('quarantines an authenticated lease before a concurrent TS recovery claim can strand release',async()=>{const dir=tmp(),watchdog=compile(dir),endpoint=pinnedEndpoint(dir),docker=path.join(dir,'docker'),lease=path.join(dir,'lease'),token='1'.repeat(32),claim='2'.repeat(32);fs.mkdirSync(lease,{mode:0o700});fs.writeFileSync(path.join(lease,'lease.json'),JSON.stringify({token}),{mode:0o600});fs.writeFileSync(path.join(lease,'lease.token'),token+'\n',{mode:0o600});fs.writeFileSync(path.join(lease,'.recovery'),JSON.stringify({pid:process.pid,processIdentity:null,token:claim,createdAt:Date.now()})+'\n',{mode:0o600});fs.writeFileSync(docker,'#!/bin/sh\nif [ "$3" = ps ]; then exit 0; fi\nexit 0\n',{mode:0o755});const owner=spawn('/bin/sleep',['0.05'],{stdio:'ignore'}),child=spawn(watchdog,['--owner',String(owner.pid),'--deadline',String(Math.ceil((Date.now()+10_000)/1000)),'--run-dir',dir,'--docker',docker,'--endpoint',endpoint.uri,'--socket-device',String(endpoint.device),'--socket-inode',String(endpoint.inode),'--run-label','release-race','--lease-path',lease,'--lease-token',token],{stdio:'ignore'}),closed=new Promise<number|null>(resolve=>child.on('close',resolve));const outcome=await Promise.race([closed,Bun.sleep(5000).then(()=>-999)]);if(outcome===-999)child.kill('SIGKILL');expect(outcome).toBe(0);expect(fs.existsSync(lease)).toBe(false);expect(fs.readdirSync(dir).some(name=>name.includes('.watchdog-release-'))).toBe(false);expect(fs.readFileSync(path.join(dir,'watchdog.event'),'utf8')).toContain('cleanup complete');});
test('attempt mode removes a patched execution copy after supervisor death',async()=>{const run=tmp(),watchdog=compile(run),control=path.join(run,'supervision','attempt'),work=path.join(run,'verification','attempt');fs.mkdirSync(control,{recursive:true});fs.mkdirSync(work,{recursive:true});fs.writeFileSync(path.join(work,'patched-source'),'sensitive execution copy');const owner=spawn('/bin/sleep',['1'],{stdio:'ignore'}),child=spawn(watchdog,['--attempt-owner',String(owner.pid),'--deadline',String(Math.ceil((Date.now()+10_000)/1000)),'--control-dir',control,'--work-root',work,'--run-root',run],{stdio:'ignore'}),closed=new Promise<number|null>(resolve=>child.on('close',resolve));expect(await closed).toBe(0);expect(fs.existsSync(work)).toBe(false);expect(fs.readFileSync(path.join(control,'attempt.event'),'utf8')).toContain('execution-copy cleanup complete');});
test('attempt cleanup cannot remove the concurrent Docker watchdog journal',async()=>{const run=tmp(),watchdog=compile(run),endpoint=pinnedEndpoint(run),attemptControl=path.join(run,'supervision','attempt'),dockerControl=path.join(attemptControl,'docker-groups','before'),work=path.join(run,'verification','attempt'),lease=path.join(run,'lease'),log=path.join(dockerControl,'docker.log'),docker=path.join(dockerControl,'docker'),id='a'.repeat(64),race='e'.repeat(64),token='c'.repeat(32);for(const dir of [attemptControl,dockerControl,work,lease])fs.mkdirSync(dir,{recursive:true});fs.writeFileSync(path.join(work,'patched-source'),'sensitive execution copy');fs.writeFileSync(path.join(lease,'lease.json'),'{}');fs.writeFileSync(path.join(lease,'lease.token'),token+'\n');fs.writeFileSync(path.join(dockerControl,'resources.journal'),`container:${id}\n`,{mode:0o600});fs.writeFileSync(docker,`#!/bin/sh\nif [ "$3" = ps ]; then if [ "$8" = "id=${id}" ]; then printf '%s\\n' '${id}'; else printf '%s\\n' '${race}'; fi; exit 0; fi\nprintf '%s\\n' "$*" >> '${log}'\n`,{mode:0o755});const owner=spawn('/bin/sleep',['1'],{stdio:'ignore'}),attempt=spawn(watchdog,['--attempt-owner',String(owner.pid),'--deadline',String(Math.ceil((Date.now()+10_000)/1000)),'--control-dir',attemptControl,'--work-root',work,'--run-root',run],{stdio:'ignore'}),containers=spawn(watchdog,['--owner',String(owner.pid),'--deadline',String(Math.ceil((Date.now()+10_000)/1000)),'--run-dir',dockerControl,'--docker',docker,'--endpoint',endpoint.uri,'--socket-device',String(endpoint.device),'--socket-inode',String(endpoint.inode),'--run-label','test-label','--lease-path',lease,'--lease-token',token],{stdio:'ignore'});const closed=(child:any)=>new Promise<number|null>(resolve=>child.on('close',resolve));expect(await Promise.all([closed(attempt),closed(containers)])).toEqual([0,0]);expect(fs.existsSync(work)).toBe(false);expect(fs.readFileSync(log,'utf8')).toContain(`rm --force --volumes ${id}`);expect(fs.existsSync(lease)).toBe(false);expect(fs.readFileSync(path.join(dockerControl,'watchdog.event'),'utf8')).toContain('cleanup complete');});
test('a terminal marker prevents any cleanup call',async()=>{const dir=tmp(),watchdog=compile(dir),endpoint=pinnedEndpoint(dir),log=path.join(dir,'docker.log'),docker=path.join(dir,'docker'),lease=path.join(dir,'lease'),token='d'.repeat(32);fs.mkdirSync(lease);fs.writeFileSync(path.join(lease,'lease.json'),'{}');fs.writeFileSync(path.join(lease,'lease.token'),token+'\n');fs.writeFileSync(docker,`#!/bin/sh\ntouch '${log}'\n`,{mode:0o755});fs.writeFileSync(path.join(dir,'resources.journal'),`container:${'b'.repeat(64)}\n`);fs.writeFileSync(path.join(dir,'watchdog.terminal'),'done\n');const r=spawnSync(watchdog,['--owner',String(process.pid),'--deadline',String(Math.ceil((Date.now()+10_000)/1000)),'--run-dir',dir,'--docker',docker,'--endpoint',endpoint.uri,'--socket-device',String(endpoint.device),'--socket-inode',String(endpoint.inode),'--run-label','test-label','--lease-path',lease,'--lease-token',token],{timeout:30_000});expect(r.status).toBe(0);expect(fs.existsSync(log)).toBe(false);});
test('acknowledges terminal cleanup well inside the caller deadline',async()=>{const dir=tmp(),watchdog=compile(dir),endpoint=pinnedEndpoint(dir),docker=path.join(dir,'docker'),lease=path.join(dir,'lease'),token='9'.repeat(32),ready=path.join(dir,'watchdog.ready'),stopped=path.join(dir,'watchdog.stopped');fs.mkdirSync(lease);fs.writeFileSync(path.join(lease,'lease.json'),'{}');fs.writeFileSync(path.join(lease,'lease.token'),token+'\n');fs.writeFileSync(docker,'#!/bin/sh\nexit 0\n',{mode:0o755});const child=spawn(watchdog,['--owner',String(process.pid),'--deadline',String(Math.ceil((Date.now()+10_000)/1000)),'--run-dir',dir,'--docker',docker,'--endpoint',endpoint.uri,'--socket-device',String(endpoint.device),'--socket-inode',String(endpoint.inode),'--run-label','ack-latency','--lease-path',lease,'--lease-token',token],{stdio:'ignore'}),closed=new Promise<number|null>(resolve=>child.once('close',resolve));for(let i=0;i<100&&!fs.existsSync(ready);i++)await Bun.sleep(10);expect(fs.existsSync(ready)).toBe(true);await Bun.sleep(50);const started=Date.now();fs.writeFileSync(path.join(dir,'watchdog.terminal'),'done\n');for(let i=0;i<100&&!fs.existsSync(stopped);i++)await Bun.sleep(10);expect(fs.existsSync(stopped)).toBe(true);expect(Date.now()-started).toBeLessThan(500);expect(await closed).toBe(0);});
test('a torn final journal row cannot retain the machine lease after exact label sweeps',async()=>{const dir=tmp(),watchdog=compile(dir),endpoint=pinnedEndpoint(dir),docker=path.join(dir,'docker'),lease=path.join(dir,'lease'),token='e'.repeat(32),id='a'.repeat(64);fs.mkdirSync(lease);fs.writeFileSync(path.join(lease,'lease.json'),'{}');fs.writeFileSync(path.join(lease,'lease.token'),token+'\n');fs.writeFileSync(docker,'#!/bin/sh\nif [ "$3" = ps ]; then exit 0; fi\nexit 0\n',{mode:0o755});fs.writeFileSync(path.join(dir,'resources.journal'),`container:${id}\ncontainer:abcd`,{mode:0o600});const owner=spawn('/bin/sleep',['1'],{stdio:'ignore'}),child=spawn(watchdog,['--owner',String(owner.pid),'--deadline',String(Math.ceil((Date.now()+10_000)/1000)),'--run-dir',dir,'--docker',docker,'--endpoint',endpoint.uri,'--socket-device',String(endpoint.device),'--socket-inode',String(endpoint.inode),'--run-label','test-label','--lease-path',lease,'--lease-token',token],{stdio:'ignore'});expect(await new Promise<number|null>(resolve=>child.on('close',resolve))).toBe(0);expect(fs.existsSync(lease)).toBe(false);expect(fs.readFileSync(path.join(dir,'watchdog.event'),'utf8')).toContain('malformed journal ignored after two exact label sweeps');});
test('a replaced Docker socket blocks cleanup and lease release',async()=>{const dir=tmp(),watchdog=compile(dir),endpoint=pinnedEndpoint(dir),docker=path.join(dir,'docker'),lease=path.join(dir,'lease'),token='f'.repeat(32),log=path.join(dir,'docker.log');fs.mkdirSync(lease);fs.writeFileSync(path.join(lease,'lease.json'),'{}');fs.writeFileSync(path.join(lease,'lease.token'),token+'\n');fs.writeFileSync(docker,`#!/bin/sh\ntouch '${log}'\nexit 0\n`,{mode:0o755});const owner=spawn('/bin/sleep',['30'],{stdio:'ignore'}),child=spawn(watchdog,['--owner',String(owner.pid),'--deadline',String(Math.ceil((Date.now()+30_000)/1000)),'--run-dir',dir,'--docker',docker,'--endpoint',endpoint.uri,'--socket-device',String(endpoint.device),'--socket-inode',String(endpoint.inode),'--run-label','test-label','--lease-path',lease,'--lease-token',token],{stdio:'ignore'});for(let i=0;i<100&&!fs.existsSync(path.join(dir,'watchdog.ready'));i++)await Bun.sleep(10);expect(fs.existsSync(path.join(dir,'watchdog.ready'))).toBe(true);const socketPath=endpoint.uri.slice('unix://'.length);fs.renameSync(socketPath,`${socketPath}.old`);const replacement=pinnedEndpoint(dir);expect(replacement.inode).not.toBe(endpoint.inode);owner.kill('SIGKILL');for(let i=0;i<50&&!fs.existsSync(path.join(dir,'watchdog.event'));i++)await Bun.sleep(100);expect(fs.readFileSync(path.join(dir,'watchdog.event'),'utf8')).toContain('socket identity changed');expect(fs.existsSync(lease)).toBe(true);expect(fs.existsSync(log)).toBe(false);child.kill('SIGKILL');await new Promise(resolve=>child.once('close',resolve));});
});
+345
View File
@@ -0,0 +1,345 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { createHash } from 'node:crypto';
import { spawn, spawnSync } from 'node:child_process';
const ROOT = path.resolve(import.meta.dir, '..');
const windows = process.platform === 'win32';
const required = process.env.GSTACK_CSO_WINDOWS_TESTS === '1';
if (required && !windows) throw new Error('GSTACK_CSO_WINDOWS_TESTS=1 requires native Windows; emulation does not qualify the launcher.');
let temporary = '', invocationCwd = '', launcher = '', core = '', marker = '', preload = '', publisher = '', buildStage = '';
beforeAll(() => {
if (!windows) return;
const installed = path.join(ROOT, 'bin', 'gstack-cso-launcher.exe');
if (!fs.existsSync(installed)) throw new Error('Native CSO launcher is missing; run bun run build:cso with MSVC first.');
temporary = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'CSO launcher 空間 '));
launcher = path.join(temporary, 'gstack-cso-launcher.exe');
core = path.join(temporary, 'gstack-cso-core.exe');
invocationCwd = path.join(temporary, 'audited cwd');
fs.mkdirSync(invocationCwd);
marker = path.join(temporary, 'preload-ran');
preload = path.join(temporary, 'preload.ts');
fs.writeFileSync(preload, `import {writeFileSync} from 'node:fs'; writeFileSync(${JSON.stringify(marker)}, 'startup injection');`);
const source = path.join(temporary, 'core-fixture.ts');
fs.writeFileSync(source, `import {existsSync,writeFileSync} from 'node:fs';
const hold=process.argv.indexOf('--hold-until');
if(hold>=0){writeFileSync(process.argv[hold+1],String(process.pid));while(!existsSync(process.argv[hold+2]))await Bun.sleep(10);writeFileSync(process.argv[hold+3],'exited');}
process.stdout.write(JSON.stringify({argv:process.argv.slice(2),env:process.env,cwd:process.cwd()}));
if(process.argv.includes('--exit-23'))process.exit(23);`);
const compiled = spawnSync(process.execPath, ['build', '--compile', '--no-compile-autoload-dotenv', '--no-compile-autoload-bunfig', '--no-compile-autoload-tsconfig', '--no-compile-autoload-package-json', source, '--outfile', core], { encoding: 'utf8', timeout: 60_000 });
expect(compiled.status).toBe(0);
const digest = createHash('sha256').update(fs.readFileSync(core)).digest('hex');
buildStage = fs.mkdtempSync(path.join(ROOT, 'bin', '.gstack-cso-stage.windows-launcher-test.'));
const stagedLauncher = path.join(buildStage, 'gstack-cso-launcher.exe');
const stagedPublisher = path.join(buildStage, 'gstack-cso-publish-lock.exe');
const native = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
'-File', path.join(ROOT, 'scripts', 'build-cso-windows.ps1'), '-RepoRoot', ROOT,
'-OutputPath', stagedLauncher, '-LockOutputPath', stagedPublisher, '-CoreSha256', digest, '-GitExePath', Bun.which('git')!],
{ encoding: 'utf8', timeout: 60_000 });
if (native.status !== 0) throw new Error(`Fixture launcher compilation failed: ${native.stdout}${native.stderr}`);
fs.copyFileSync(stagedLauncher, launcher);
publisher = path.join(temporary, 'gstack-cso-publish-lock.exe');
fs.copyFileSync(stagedPublisher, publisher);
fs.writeFileSync(path.join(temporary, '.gstack-cso-generation.lock'), '');
fs.writeFileSync(path.join(temporary, '.gstack-cso-generation'), `${digest}\n`);
fs.rmSync(buildStage, { recursive: true, force: true }); buildStage = '';
}, 140_000);
afterAll(() => {
if (buildStage) fs.rmSync(buildStage, { recursive: true, force: true });
if (temporary) fs.rmSync(temporary, { recursive: true, force: true });
});
function launchEnvironment(extra: Record<string, string> = {}) {
return { ...process.env, HOME: temporary, GSTACK_HOME: path.join(temporary, 'state'), PATH: temporary,
NODE_OPTIONS: '--require=hostile', RUBYOPT: '-rhostile', PYTHONPATH: temporary,
GSTACK_CSO_SECRET_CANARY: 'must-not-cross-startup', ...extra };
}
function launch(args: string[], extra: Record<string, string> = {}, file = launcher) {
return spawnSync(file, args, {
cwd: invocationCwd, encoding: 'utf8', timeout: 30_000,
env: launchEnvironment(extra),
});
}
async function waitForFile(file: string, timeout = 10_000) {
const deadline = Date.now() + timeout;
while (!fs.existsSync(file) && Date.now() < deadline) await Bun.sleep(10);
expect(fs.existsSync(file)).toBe(true);
}
function publishProbe() {
return spawnSync(publisher, [temporary, process.execPath, '--version'], { encoding: 'utf8', timeout: 10_000 });
}
function pathIdentity(value: string) {
const stat = fs.statSync(value, { bigint: true });
return { device: stat.dev, file: stat.ino };
}
function expectSuccessfulProcess(result: ReturnType<typeof spawnSync>, label: string) {
if (result.status === 0) return;
const bounded = (value: unknown) => String(value ?? '').slice(0, 8192);
throw new Error(`${label} failed: ${JSON.stringify({
status: result.status,
signal: result.signal,
error: result.error?.message,
stdout: bounded(result.stdout),
stderr: bounded(result.stderr),
})}`);
}
function filesNamed(root: string, name: string): string[] {
const found: string[] = [];
const walk = (directory: string) => {
let entries: fs.Dirent[];
try { entries = fs.readdirSync(directory, { withFileTypes: true }); } catch { return; }
for (const entry of entries) {
const candidate = path.join(directory, entry.name);
if (entry.isSymbolicLink()) continue;
if (entry.isDirectory()) walk(candidate);
else if (entry.isFile() && entry.name === name) found.push(candidate);
}
};
walk(root);
return found;
}
describe('CSO native Windows build contract', () => {
test('Windows builds use MSVC with a static CRT and no Bun-hosted public launcher', () => {
const build = fs.readFileSync(path.join(ROOT, 'scripts/build-cso.sh'), 'utf8');
const msvc = fs.readFileSync(path.join(ROOT, 'scripts/build-cso-windows.ps1'), 'utf8');
expect(build).toContain('powershell.exe -NoProfile -NonInteractive');
expect(build).toContain('build-cso-windows.ps1');
expect(build).toContain('-OutputPath "$(cygpath -w "$CSO_STAGE_LAUNCHER")"');
expect(build).toContain('-LockOutputPath "$(cygpath -w "$CSO_STAGE_LOCKER")"');
expect(build).toContain('-CoreSha256 "$CSO_CORE_SHA256"');
expect(build).toContain('-GitExePath "$(cygpath -aw "$CSO_WINDOWS_GIT")"');
expect(build).toContain('CSO_PUBLISH_SHELL="$(cygpath -aw /usr/bin/bash.exe)"');
expect(build).not.toContain('launcher-windows.ts');
expect(fs.existsSync(path.join(ROOT, 'lib/cso/launcher-windows.ts'))).toBe(false);
expect(msvc).toContain('Launch-VsDevShell.ps1');
expect(msvc).toContain('[switch]$CheckOnly');
expect(msvc).toContain('Normal CSO Windows builds require staged outputs and a lowercase SHA-256 core binding.');
expect(msvc).toContain("[System.IO.FileAttributes]::ReparsePoint");
expect(msvc).not.toContain("Join-Path $RepoRoot 'bin\\gstack-cso-launcher.exe'");
expect(msvc).toContain("Set-Content -LiteralPath $source");
expect(msvc).toContain('-Arch amd64 -HostArch amd64 -SkipAutomaticLocation');
expect(msvc).toContain('StartsWith($installationPrefix');
expect(msvc).toContain('/MT');
expect(msvc).toContain('/W4 /WX');
expect(msvc).toContain('GSTACK_CSO_CORE_SHA256');
expect(msvc).toContain('GSTACK_CSO_GIT_PATH');
expect(msvc).toContain('/FI$binding');
expect(msvc).toContain('if ($LASTEXITCODE -ne 0)');
const processSource=fs.readFileSync(path.join(ROOT,'lib','cso','process.ts'),'utf8');
expect(processSource).toContain("includeNullPath=process.platform==='win32'?'/dev/null':nullPath");
const launcherSource = fs.readFileSync(path.join(ROOT, 'lib/cso/launcher-windows.c'), 'utf8');
expect(launcherSource).toContain('.gstack-cso-generation.lock');
expect(launcherSource).toContain('.gstack-cso-generation');
expect(launcherSource).toContain('GSTACK_CSO_CORE_SHA256');
expect(launcherSource).toContain('BCryptOpenAlgorithmProvider');
expect(launcherSource).toContain('MS_PRIMITIVE_PROVIDER');
expect(launcherSource).toContain('sha256_handle(pinned_core');
expect(launcherSource).toContain('#pragma comment(lib, "bcrypt.lib")');
expect(launcherSource).toContain('PROC_THREAD_ATTRIBUTE_HANDLE_LIST');
expect(launcherSource).toContain('joined_path');
const childWait = launcherSource.indexOf('WaitForSingleObject(child.hProcess, INFINITE)');
expect(childWait).toBeGreaterThan(0);
expect(launcherSource.lastIndexOf('CloseHandle(pinned_core)')).toBeGreaterThan(childWait);
expect(launcherSource.lastIndexOf('CloseHandle(generation_gate)')).toBeGreaterThan(childWait);
const publisherSource = fs.readFileSync(path.join(ROOT, 'lib/cso/publish-lock.c'), 'utf8');
expect(publisherSource).toContain('joined_path');
expect(publisherSource).toContain('GENERIC_READ | GENERIC_WRITE, 0');
});
test('Windows CI runs the actual launcher tests rather than marking the platform supported from a source check', () => {
const workflow = Bun.YAML.parse(fs.readFileSync(path.join(ROOT, '.github/workflows/free-tests.yml'), 'utf8')) as any;
const job = workflow.jobs['cso-windows-launcher'];
expect(job['runs-on']).toBe('windows-latest');
expect(job.steps.some((s: any) => s.run === 'bun run build:cso' && s.shell === 'bash')).toBe(true);
const smoke = job.steps.find((s: any) => s.run === 'bun run test:cso:windows');
expect(smoke.env.GSTACK_CSO_WINDOWS_TESTS).toBe('1');
expect(smoke['continue-on-error']).not.toBe(true);
});
test.skipIf(!windows)('Windows build refuses output path escapes before invoking MSVC',()=>{
const script=path.join(ROOT,'scripts/build-cso-windows.ps1'),outside=fs.mkdtempSync(path.join(os.tmpdir(),'cso-bin-evil-'));
const stage=fs.mkdtempSync(path.join(ROOT,'bin','.gstack-cso-stage.path-test.'));
const validOutput=path.join(stage,'gstack-cso-launcher.exe'),validLock=path.join(stage,'gstack-cso-publish-lock.exe'),digest='a'.repeat(64);
try{
for(const [output,lock] of [[path.join(outside,'gstack-cso-launcher.exe'),validLock],[path.join(stage,'..','gstack-cso-launcher.exe'),validLock],[path.join(stage,'wrong.exe'),validLock],[validOutput,path.join(outside,'gstack-cso-publish-lock.exe')],[validOutput,path.join(stage,'wrong-lock.exe')]]){
const result=spawnSync('powershell.exe',['-NoProfile','-NonInteractive','-ExecutionPolicy','Bypass','-File',script,'-RepoRoot',ROOT,'-OutputPath',output,'-LockOutputPath',lock,'-CoreSha256',digest,'-GitExePath',Bun.which('git')!],{encoding:'utf8',timeout:30_000});
expect(result.status).not.toBe(0);expect(`${result.stdout}${result.stderr}`).toContain('direct, non-reparse staging directory');
}
}finally{fs.rmSync(stage,{recursive:true,force:true});fs.rmSync(outside,{recursive:true,force:true});}
});
});
(windows ? describe : describe.skip)('CSO native Windows startup', () => {
test('BUN_OPTIONS cannot execute a preload before the environment is scrubbed', () => {
const result = launch(['--version'], { BUN_OPTIONS: `--preload=${preload}` });
expect(result.status).toBe(0);
expect(fs.existsSync(marker)).toBe(false);
const value = JSON.parse(result.stdout);
expect(value.argv).toEqual(['--version']);
expect(value.env.BUN_OPTIONS).toBeUndefined();
expect(value.env.GSTACK_CSO_SECRET_CANARY).toBeUndefined();
expect(value.env.NODE_OPTIONS).toBeUndefined();
expect(value.env.GSTACK_HOME).toBe(path.join(temporary, 'state'));
expect(value.env.PATH).not.toBe(temporary);
expect(value.env.SystemRoot.toLowerCase()).toBe(process.env.SystemRoot!.toLowerCase());
expect(pathIdentity(value.cwd)).toEqual(pathIdentity(temporary));
expect(pathIdentity(value.cwd)).not.toEqual(pathIdentity(invocationCwd));
});
test('BUN_BE_BUN cannot turn the public command into the Bun runtime', () => {
const result = launch(['--version'], { BUN_BE_BUN: '1' });
expect(result.status).toBe(0);
const value = JSON.parse(result.stdout);
expect(value.argv).toEqual(['--version']);
expect(value.env.BUN_BE_BUN).toBeUndefined();
});
test('preserves Unicode, empty, quoted, trailing-backslash and shell-looking arguments', () => {
const args = ['', '空間', 'with spaces', 'a"b', 'C:\\directory with spaces\\', '\\"', '& whoami', '%PATH%', 'line\nbreak'];
const result = launch(args);
expect(result.status).toBe(0);
expect(JSON.parse(result.stdout).argv).toEqual(args);
});
test('forwards child exit status and fails closed on oversized inherited input', () => {
expect(launch(['--exit-23']).status).toBe(23);
const result = launch([], { GSTACK_HOME: 'x'.repeat(8193) });
expect(result.status).toBe(69);
expect(result.stdout).toBe('');
expect(result.stderr).toContain('environment input is too large');
});
test('resolves launcher installation aliases before locating its sibling core', () => {
const alias = path.join(temporary, 'alias');
fs.symlinkSync(temporary, alias, 'junction');
const result = launch(['--version'], {}, path.join(alias, 'gstack-cso-launcher.exe'));
expect(result.status).toBe(0);
expect(JSON.parse(result.stdout).argv).toEqual(['--version']);
fs.unlinkSync(alias);
});
test('rejects a launcher/manifest generation mismatch before executing the core', () => {
const mismatched = path.join(temporary, 'mismatched generation');
fs.mkdirSync(mismatched);
fs.copyFileSync(launcher, path.join(mismatched, 'gstack-cso-launcher.exe'));
fs.copyFileSync(core, path.join(mismatched, 'gstack-cso-core.exe'));
fs.writeFileSync(path.join(mismatched, '.gstack-cso-generation.lock'), '');
fs.writeFileSync(path.join(mismatched, '.gstack-cso-generation'), `${'0'.repeat(64)}\n`);
const result = launch(['--version'], {}, path.join(mismatched, 'gstack-cso-launcher.exe'));
expect(result.status).toBe(69);
expect(result.stdout).toBe('');
expect(result.stderr).toContain('generations do not match');
});
test('rejects changed core bytes even when the generation manifest is unchanged', () => {
const tampered = path.join(temporary, 'tampered core');
fs.mkdirSync(tampered);
const tamperedLauncher = path.join(tampered, 'gstack-cso-launcher.exe');
const tamperedCore = path.join(tampered, 'gstack-cso-core.exe');
fs.copyFileSync(launcher, tamperedLauncher);
fs.copyFileSync(core, tamperedCore);
fs.appendFileSync(tamperedCore, Buffer.from([0]));
fs.writeFileSync(path.join(tampered, '.gstack-cso-generation.lock'), '');
fs.copyFileSync(path.join(temporary, '.gstack-cso-generation'), path.join(tampered, '.gstack-cso-generation'));
const result = launch(['--version'], {}, tamperedLauncher);
expect(result.status).toBe(69);
expect(result.stdout).toBe('');
expect(result.stderr).toContain('digest does not match');
});
test('the inherited generation gate survives launcher termination until the core exits', async () => {
const ready = path.join(temporary, 'held-core.ready'), release = path.join(temporary, 'held-core.release'), exited = path.join(temporary, 'held-core.exited');
const running = spawn(launcher, ['--hold-until', ready, release, exited], { cwd: invocationCwd, env: launchEnvironment(), stdio: 'ignore' });
try {
await waitForFile(ready);
const launcherExited = new Promise<void>(resolveExit => running.once('exit', () => resolveExit()));
running.kill('SIGKILL');
await launcherExited;
expect(publishProbe().status).toBe(73);
fs.writeFileSync(release, 'release');
const deadline = Date.now() + 10_000; let result = publishProbe();
while (result.status === 73 && Date.now() < deadline) { await Bun.sleep(20); result = publishProbe(); }
expect(result.status).toBe(0);
} finally {
fs.writeFileSync(release, 'release');
if (!running.killed) running.kill('SIGKILL');
const deadline = Date.now() + 5_000;
while (!fs.existsSync(exited) && Date.now() < deadline) await Bun.sleep(10);
}
}, 30_000);
test('a missing core never falls back to a PATH executable or Bun', () => {
const missing = path.join(temporary, 'missing core');
fs.mkdirSync(missing);
const copy = path.join(missing, 'gstack-cso-launcher.exe');
fs.copyFileSync(launcher, copy);
fs.writeFileSync(path.join(missing, '.gstack-cso-generation.lock'), '');
fs.copyFileSync(path.join(temporary, '.gstack-cso-generation'), path.join(missing, '.gstack-cso-generation'));
const result = launch(['--version'], {}, copy);
expect(result.status).toBe(69);
expect(result.stdout).toBe('');
expect(result.stderr).toContain('trusted compiled helper is missing');
});
test('the actual helper finds trusted Git and stores state under USERPROFILE without HOME', () => {
const repository=path.join(temporary,'actual repository'),profile=path.join(temporary,'profile');
fs.mkdirSync(repository);fs.mkdirSync(profile);
const git='C:\\Program Files\\Git\\cmd\\git.exe',gitEnv={...process.env,HOME:profile};
for(const args of [['init','-q'],['config','user.email','fixture@example.test'],['config','user.name','Fixture']] as string[][]){const result=spawnSync(git,args,{cwd:repository,encoding:'utf8',env:gitEnv,timeout:10_000});expect(result.status).toBe(0);}
fs.writeFileSync(path.join(repository,'app.js'),'console.log("safe")\n');
for(const args of [['add','app.js'],['commit','-qm','fixture']] as string[][]){const result=spawnSync(git,args,{cwd:repository,encoding:'utf8',env:gitEnv,timeout:10_000});expect(result.status).toBe(0);}
const actual=path.join(ROOT,'bin','gstack-cso-launcher.exe'),env={...process.env,HOME:'',GSTACK_HOME:'',CLAUDE_PLUGIN_ROOT:'',CLAUDE_PLUGIN_DATA:'',USERPROFILE:profile,PATH:temporary,NODE_OPTIONS:'--require=hostile'};
const doctor=spawnSync(actual,['doctor','--repo',repository],{cwd:repository,encoding:'utf8',env,timeout:30_000});expect(doctor.status).toBe(0);expect(JSON.parse(doctor.stdout).downloads).toBe(false);
const started=spawnSync(actual,['start','--repo',repository,'--offline'],{cwd:repository,encoding:'utf8',env,timeout:30_000});expectSuccessfulProcess(started,'gstack-cso start');expect(JSON.parse(started.stdout).schemaVersion).toBe(3);expect(fs.existsSync(path.join(profile,'.gstack','security','cso'))).toBe(true);
});
test('the actual helper rejects source mutation during snapshot capture without certifying a report', async () => {
const repository=path.join(temporary,'racing repository'),profile=path.join(temporary,'race profile'),padding=path.join(repository,'padding'),target=path.join(repository,'zzzz-race-target.js');
fs.mkdirSync(repository);fs.mkdirSync(profile);fs.mkdirSync(padding);
const git='C:\\Program Files\\Git\\cmd\\git.exe',gitEnv={...process.env,HOME:profile};
for(const args of [['init','-q'],['config','user.email','fixture@example.test'],['config','user.name','Fixture']] as string[][]){const result=spawnSync(git,args,{cwd:repository,encoding:'utf8',env:gitEnv,timeout:10_000});expect(result.status).toBe(0);}
const sourceBytes=128*1024,stable=Buffer.alloc(sourceBytes,0x61),changed=Buffer.alloc(sourceBytes,0x62);
fs.writeFileSync(target,stable);
for(let index=0;index<192;index++)fs.writeFileSync(path.join(padding,`${String(index).padStart(4,'0')}.js`),stable);
for(const args of [['add',path.basename(target)],['commit','-qm','fixture']] as string[][]){const result=spawnSync(git,args,{cwd:repository,encoding:'utf8',env:gitEnv,timeout:30_000});expect(result.status).toBe(0);}
const actual=path.join(ROOT,'bin','gstack-cso-launcher.exe'),env={...process.env,HOME:'',GSTACK_HOME:'',CLAUDE_PLUGIN_ROOT:'',CLAUDE_PLUGIN_DATA:'',USERPROFILE:profile,PATH:temporary},state=path.join(profile,'.gstack','security','cso');
const child=spawn(actual,['start','--repo',repository,'--offline'],{cwd:repository,env,stdio:['ignore','pipe','pipe']});
let stdout='',stderr='',closed=false;
child.stdout.on('data',chunk=>{stdout=(stdout+String(chunk)).slice(-8192);});
child.stderr.on('data',chunk=>{stderr=(stderr+String(chunk)).slice(-8192);});
const terminal=new Promise<{code:number|null;error?:string}>(resolve=>{
child.once('error',error=>{closed=true;resolve({code:null,error:error.message});});
child.once('close',code=>{closed=true;resolve({code});});
});
const markerDeadline=Date.now()+30_000;let mutations=0,outcome:{code:number|null;error?:string}|undefined;
try{
while(!closed&&!filesNamed(state,'history-status.json').length&&Date.now()<markerDeadline)await Bun.sleep(2);
if(!filesNamed(state,'history-status.json').length)throw new Error('gstack-cso exited or timed out before reaching the bounded snapshot mutation point');
while(!closed&&Date.now()<markerDeadline+15_000){
try{fs.writeFileSync(target,changed);mutations++;}catch(error:any){if(!['EBUSY','EACCES','EPERM'].includes(error?.code))throw error;}
await Bun.sleep(1);
}
if(!closed)throw new Error('gstack-cso did not finish after the injected snapshot mutation');
outcome=await terminal;
}finally{
if(!closed){spawnSync('taskkill',['/PID',String(child.pid),'/T','/F'],{encoding:'utf8',timeout:10_000});await Promise.race([terminal,Bun.sleep(10_000)]);}
}
expect(mutations).toBeGreaterThan(0);
expect(outcome?.error).toBeUndefined();
expect(outcome?.code).not.toBe(0);
expect(stderr).toContain('SNAPSHOT_RACE');
expect(stdout).toBe('');
expect(filesNamed(state,'snapshot.json')).toEqual([]);
expect(filesNamed(state,'report.json')).toEqual([]);
}, 90_000);
});
+71
View File
@@ -0,0 +1,71 @@
import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { generateKeyPairSync } from 'node:crypto';
import { AssertionWitnessBinding, CsoError, VerificationObservation, canonical, sha256 } from '../lib/cso/contracts';
import { canonicalStartPlan, canonicalTestPlan, patchHash, treeHash, validateRepairBundle, verifyRepair } from '../lib/cso/verification';
import { AssertionWitnessSession, assertionWitnessReplayHash, testExecutionPassed, validateStoredAssertionWitnessReceipt } from '../lib/cso/witness';
const roots:string[]=[];
const temporary=()=>{const root=fs.mkdtempSync(path.join(os.tmpdir(),'cso-witness-'));roots.push(root);return root;};
const H=(value:string)=>sha256(value);
const tap=`TAP version 13
# Subtest: legitimate control remains available
ok 1 - legitimate control remains available
---
duration_ms: 1
...
1..1
# tests 1
# suites 0
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
`;
afterEach(()=>{for(const root of roots.splice(0))fs.rmSync(root,{recursive:true,force:true});});
function stable(phase:'before'|'after'):Omit<AssertionWitnessBinding,'schemaVersion'|'protocol'|'nonce'|'issuedAt'|'expiresAt'>{
return{phase,runId:'witness-run',findingId:'a'.repeat(32),policyHash:H('policy'),auditPolicyHash:H('audit-policy'),runtime:{image:`runtime@sha256:${'b'.repeat(64)}`,verifierImage:`runtime@sha256:${'b'.repeat(64)}`,platform:'linux/amd64',profile:'node'},runner:{testToolchain:'runtime',startPlanHash:H('start'),testPlanHash:H('tests'),commandsHash:H(canonical([{executable:'/usr/local/bin/node',args:['--test','--test-reporter=tap','./app.test.js']}])),minimumPassingTestsHash:H(canonical([1]))},sourceHash:H(`source-${phase}`),dependencyHash:H(`dependency-${phase}`),configurationHash:H(`configuration-${phase}`),requestHash:H('request'),patchHash:H('patch'),harnessHash:H('harness'),assertionHash:H('assertions'),fixturesHash:H('fixtures')};
}
describe('CSO authenticated external assertion witness',()=>{
test('rejects forged, stale, and mismatched receipts after a valid out-of-process attestation',async()=>{
const work=temporary(),session=new AssertionWitnessSession(work,Date.now()+60_000),handle=session.handle(stable('before')),observation:VerificationObservation={booted:true,legitimate:true,security:'intended_failure',existingTests:false,output:'external verifier passed',inputHash:''},command={executable:'/usr/local/bin/node',args:['--test','--test-reporter=tap','./app.test.js']};
const receipt=await handle.attest(observation,[{command,code:0,output:tap,minimumPassingTests:1}]);
expect(receipt).toMatchObject({externalAssertionsPassed:true,diagnosticTestsPassed:true,binding:{phase:'before',policyHash:H('policy'),runner:{testToolchain:'runtime'}}});expect(validateStoredAssertionWitnessReceipt(receipt).keyId).toBe(session.keyId);
expect(()=>handle.validate({...receipt,signature:`${receipt.signature.slice(0,-1)}${receipt.signature.endsWith('0')?'1':'0'}`},observation)).toThrow('signature');
const other=session.handle(stable('after'));expect(()=>other.validate(receipt,observation)).toThrow('does not bind');
expect(()=>handle.validate(receipt,observation,Date.parse(receipt.binding.expiresAt)+1)).toThrow('stale');
expect(()=>handle.validate(receipt,{...observation,legitimate:false})).toThrow('observation');
});
test('the native launcher routes the private compiled witness child protocol',()=>{
const root=temporary(),launcher=path.resolve(import.meta.dir,'../bin',process.platform==='win32'?'gstack-cso-launcher.exe':'gstack-cso-launcher'),core=path.resolve(import.meta.dir,'../bin',process.platform==='win32'?'gstack-cso-core.exe':'gstack-cso-core');
const direct=spawnSync(core,['--version'],{cwd:root,encoding:'utf8',env:{...process.env,GSTACK_CSO_GENERATION_LOCK_FD:''},timeout:30_000});expect(direct.status).not.toBe(0);expect(direct.stderr).toContain('Direct use of the internal CSO payload is unsupported');
const pair=generateKeyPairSync('ed25519'),privateKey=pair.privateKey.export({format:'pem',type:'pkcs8'}).toString(),publicKey=pair.publicKey.export({format:'der',type:'spki'}).toString('hex'),now=Date.now(),command={executable:'/usr/local/bin/node',args:['--test','--test-reporter=tap','./app.test.js']},binding={schemaVersion:1,protocol:'gstack-cso-assertion-witness-v1',nonce:H('compiled nonce'),issuedAt:new Date(now).toISOString(),expiresAt:new Date(now+60_000).toISOString(),...stable('before')},observation:VerificationObservation={booted:true,legitimate:true,security:'intended_failure',existingTests:false,output:'compiled external verifier passed',inputHash:''},child=spawnSync(launcher,['__cso-assertion-witness'],{cwd:root,encoding:'utf8',env:{HOME:root,GSTACK_HOME:path.join(root,'state'),PATH:'/usr/bin:/bin'},input:JSON.stringify({privateKey,publicKey,binding,observation,executions:[{command,code:0,output:tap,minimumPassingTests:1}]}),timeout:30_000});expect(child.status).toBe(0);const receipt=validateStoredAssertionWitnessReceipt(JSON.parse(child.stdout));expect(receipt).toMatchObject({publicKey,externalAssertionsPassed:true,diagnosticTestsPassed:true,binding:{nonce:H('compiled nonce'),phase:'before'}});
});
test('a test file that prints a forged TAP summary is rejected as a path wrapper',()=>{
const root=temporary(),file=path.join(root,'forged.test.js'),node=Bun.which('node');if(!node)throw new Error('Node is required for the CSO Node witness fixture');
fs.writeFileSync(file,"console.log('# Subtest: forged pass')\nconsole.log('ok 1 - forged pass')\nconsole.log('1..1')\nconsole.log('# tests 1')\nconsole.log('# pass 1')\nconsole.log('# fail 0')\nconsole.log('# cancelled 0')\n");
const command={executable:node,args:['--test','--test-reporter=tap','./forged.test.js']},result=spawnSync(node,command.args,{cwd:root,encoding:'utf8',timeout:30_000});expect(result.status).toBe(0);expect(result.stdout).toContain('# \\# Subtest: forged pass');expect(testExecutionPassed(command,result.status??-1,result.stdout+result.stderr,1)).toBe(false);
});
test('valid external assertions issue a runtime-tested bundle with replay-stable witness evidence',async()=>{
const runDir=temporary(),snapshot=path.join(runDir,'snapshot');fs.mkdirSync(snapshot);const files:Record<string,string>={'package.json':JSON.stringify({private:true,scripts:{start:'node app.js',test:'node --test'}})+'\n','app.js':'module.exports = "vulnerable"\n','app.test.js':"const test=require('node:test');test('legitimate control remains available',()=>{});\n"};for(const [name,body] of Object.entries(files))fs.writeFileSync(path.join(snapshot,name),body);
const start=canonicalStartPlan(snapshot,'node',3456),tests=canonicalTestPlan(snapshot,'node'),request:any={findingId:'a'.repeat(32),runtimeProfile:'node',port:3456,start:start.command,legitimate:[{name:'legitimate control',path:'/control',method:'GET',expected:{status:200,includes:'CONTROL_OK'}}],security:{name:'unauthorized secret is denied',path:'/security',method:'GET',expected:{status:403,includes:'DENIED'},vulnerable:{status:200,includes:'SECRET'}},existingTests:tests.commands,fixtures:{},boundaryFiles:['app.js'],testFiles:tests.files,changes:[{path:'app.js',beforeSha256:sha256(files['app.js']),after:'module.exports = "fixed"\n',effect:'source'}],review:{reviewer:'independent-reviewer',independent:true,rootCauseRepaired:true,featurePreserved:true,boundaryMocks:false,rationale:'The policy is repaired while the external control remains available.',reviewedPatchHash:''}};request.review.reviewedPatchHash=patchHash(request);
const entries=Object.entries(files).map(([name,body])=>({path:name,originalHash:sha256(body),executionHash:sha256(body),bytes:Buffer.byteLength(body),mode:0o600})),manifest:any={version:3,root:snapshot,createdAt:new Date().toISOString(),expiresAt:new Date(Date.now()+86_400_000).toISOString(),headCommit:'c'.repeat(40),originalHash:H('original'),executionHash:treeHash(snapshot),entries},runtime:any={id:'node',stack:'node',image:`runtime@sha256:${'b'.repeat(64)}`,platform:'linux/amd64'};
let executionCount=0;const executor={observe:async(_source:string,phase:'before'|'after',received:any,_runtime:any,_verifier:any,_work:string,_control:string,_execution:any,evidence:any,witness:any)=>{const observation:VerificationObservation={booted:true,legitimate:true,security:phase==='before'?'intended_failure':'pass',existingTests:false,output:`external ${phase} assertions passed`,inputHash:''},output=tap.replace('duration_ms: 1',`duration_ms: ${++executionCount}`),receipt=await witness.attest(observation,[{command:received.existingTests[0],code:0,output,minimumPassingTests:evidence.minimumPassingTests[0]}]);return{observation:{...observation,existingTests:receipt.diagnosticTestsPassed,inputHash:witness.binding.harnessHash},witness:receipt};}};
const first=await verifyRepair({runId:'witness-run',runDir,manifest,rawRequest:request,runtime,verifier:runtime,policyHash:H('policy'),auditPolicyHash:H('audit-policy'),archives:[],executor});expect(first.manifest).toMatchObject({result:'runtime_tested',assertionAssurance:'authenticated_out_of_process',testCompletionAssurance:'self_reported',reviewAssurance:'self_attested'});expect(first.bundle.witness?.before.publicKey).toBe(first.bundle.witness?.after.publicKey);expect(validateRepairBundle(first.bundle,first.bundle.id,snapshot,manifest).id).toBe(first.bundle.id);expect(fs.existsSync(path.join(runDir,'bundles',`${first.bundle.id}.json`))).toBe(true);const forged=structuredClone(first.bundle),signature=forged.witness!.before.signature;forged.witness!.before.signature=`${signature.slice(0,-1)}${signature.endsWith('0')?'1':'0'}`;expect(()=>validateRepairBundle(forged,forged.id,snapshot,manifest)).toThrow('signature');const reused=structuredClone(first.bundle);reused.witness!.after=reused.witness!.before;expect(()=>validateRepairBundle(reused,reused.id,snapshot,manifest)).toThrow('identity');
const replay=await verifyRepair({runId:'witness-run',runDir,manifest,rawRequest:request,runtime,verifier:runtime,policyHash:H('policy'),auditPolicyHash:H('audit-policy'),archives:[],executor,persist:false});expect(replay.manifest.witnessHash).not.toBe(first.manifest.witnessHash);expect(assertionWitnessReplayHash(replay.bundle.witness!)).toBe(assertionWitnessReplayHash(first.bundle.witness!));expect(replay.bundle.witness?.before.binding.nonce).not.toBe(first.bundle.witness?.before.binding.nonce);const immutable=(value:any)=>{const {id:_,createdAt:__,before:___,after:____,witnessHash:______,...rest}=value;return rest;};expect(canonical(immutable(replay.manifest))).toBe(canonical(immutable(first.manifest)));
});
test('a signed forged reporter diagnostic cannot mint a runtime-tested bundle',async()=>{
const root=temporary(),session=new AssertionWitnessSession(root,Date.now()+60_000),handle=session.handle(stable('before')),observation:VerificationObservation={booted:true,legitimate:true,security:'intended_failure',existingTests:false,output:'external verifier passed',inputHash:''},command={executable:'/usr/local/bin/node',args:['--test','--test-reporter=tap','./app.test.js']},forged="# Subtest: app.test.js\nok 1 - app.test.js\n1..1\n# tests 1\n# pass 1\n# fail 0\n# cancelled 0\n";
const receipt=await handle.attest(observation,[{command,code:0,output:forged,minimumPassingTests:1}]);expect(receipt.externalAssertionsPassed).toBe(true);expect(receipt.diagnosticTestsPassed).toBe(false);expect(receipt.executions[0].reportedPassed).toBe(false);
});
});
+43 -43
View File
@@ -1,65 +1,65 @@
{
"_comment": "Context-budget ratchet ceilings (~tokens). Regenerate: bun test/helpers/capture-context-budget.ts. Headroom: alwaysOnTotal x1.05, eagerPerInvocation x1.1. Graded by test/context-budget-ratchet.test.ts via lib/context-bill.ts checkBudget.",
"alwaysOnTotal": 6372,
"alwaysOnTotal": 6397,
"eagerPerInvocation": {
"autoplan": 17697,
"benchmark": 7403,
"autoplan": 18022,
"benchmark": 7657,
"benchmark-models": 3829,
"browse": 8003,
"browser-skills/hackernews-frontpage": 371,
"canary": 13522,
"canary": 13914,
"careful": 919,
"codex": 15490,
"context-restore": 9618,
"context-save": 10234,
"cso": 16110,
"design-consultation": 18392,
"design-html": 13767,
"design-review": 34483,
"design-shotgun": 13828,
"devex-review": 20302,
"codex": 15479,
"context-restore": 9607,
"context-save": 10224,
"cso": 4700,
"design-consultation": 18595,
"design-html": 14429,
"design-review": 34471,
"design-shotgun": 13856,
"devex-review": 20292,
"diagram": 4279,
"document-generate": 12362,
"document-release": 10602,
"document-generate": 12352,
"document-release": 10683,
"freeze": 990,
"gstack": 3976,
"gstack-upgrade": 4201,
"gstack-upgrade": 4586,
"guard": 889,
"health": 10816,
"investigate": 12286,
"ios-clean": 8721,
"ios-design-review": 8902,
"ios-fix": 8674,
"ios-qa": 11414,
"ios-sync": 8845,
"land-and-deploy": 18971,
"landing-report": 9527,
"learn": 9197,
"health": 10805,
"investigate": 12276,
"ios-clean": 8710,
"ios-design-review": 8891,
"ios-fix": 8664,
"ios-qa": 11403,
"ios-sync": 8834,
"land-and-deploy": 19043,
"landing-report": 9517,
"learn": 9187,
"make-pdf": 5314,
"office-hours": 22420,
"office-hours": 22374,
"open-gstack-browser": 4510,
"openclaw/skills/gstack-openclaw-ceo-review": 2764,
"openclaw/skills/gstack-openclaw-investigate": 1429,
"openclaw/skills/gstack-openclaw-office-hours": 4433,
"openclaw/skills/gstack-openclaw-retro": 2542,
"pair-agent": 11622,
"plan-ceo-review": 20550,
"plan-design-review": 20315,
"plan-devex-review": 17799,
"plan-eng-review": 14750,
"plan-tune": 14771,
"qa": 15857,
"qa-only": 16910,
"retro": 18974,
"review": 16021,
"pair-agent": 11612,
"plan-ceo-review": 20824,
"plan-design-review": 20648,
"plan-devex-review": 17884,
"plan-eng-review": 14930,
"plan-tune": 14761,
"qa": 15847,
"qa-only": 17218,
"retro": 19122,
"review": 16010,
"scrape": 6904,
"setup-browser-cookies": 3194,
"setup-deploy": 11272,
"setup-gbrain": 15565,
"ship": 20518,
"skillify": 12206,
"spec": 15011,
"sync-gbrain": 13985,
"setup-deploy": 11557,
"setup-gbrain": 15554,
"ship": 20347,
"skillify": 12196,
"spec": 14993,
"sync-gbrain": 13975,
"unfreeze": 393
}
}
+254
View File
@@ -0,0 +1,254 @@
# CSO vulnerable/fixed evaluation corpus
This corpus contains **40 immutable source pairs**: ten each for Node, Bun,
Python, and Rails. `manifest.json` pins both source hashes, expected root cause,
location, severity, and evaluation eligibility. `materialize.ts` deterministically
creates either member of each pair. Changing source requires an explicit corpus
manifest update; the loader rejects silent drift.
The ten families are SQL injection, command injection, path traversal, SSRF,
object authorization, tenant isolation, HTML injection, open redirects, mass
assignment, and resource exhaustion. Every app exposes `/action` and `/health` on
loopback port 8000. The SSRF fixtures additionally create a disposable loopback
status service on port 8001. Exhaustion assertions request 250 small records to
prove missing admission; they do not attempt to exhaust the evaluator.
Node uses standard modules, including `node:sqlite`; Bun uses `bun:sqlite`; Python
uses its standard library. Rails fixtures contain an actual Rails application
and controller, with native SQLite and Puma dependencies. Their common lock was
generated by Ruby 3.2.8 / Bundler 2.6.7 from public registry metadata only. The
exact command, lock hash, and 68 public archive hashes are recorded in
`rails-lock-provenance.json`. No fixture application was run to generate this
lock. The recorded archive hashes must be checked by runtime qualification; a
successful resolver run does not establish cold-start support.
Authentication is an explicit fixture precondition: the test adapter establishes
member-1 in tenant-a. The assessment scope is the selected endpoint's behavior.
This prevents an intentionally constant test identity from being mistaken for a
production authentication design. Application README files state legitimate
behavior; they never include attack payloads or expected finding labels.
## Preparing matched evaluations
From the repository root, export the v2 generated skill tree and build a
canonical portable payload for each version before pinning an exact model ID:
```sh
mkdir -p .context/cso-v2-source
git archive origin/main cso/SKILL.md cso/sections | \
tar -x -C .context/cso-v2-source --strip-components=1
bun scripts/cso-eval.ts payload --version v2 \
--skill-dir .context/cso-v2-source --output .context/cso-v2.payload.md
bun scripts/cso-eval.ts payload --version v3 \
--skill-dir cso --output .context/cso-v3.payload.md
bun scripts/cso-eval.ts matrix \
--model EXACT_MODEL_ID --host codex \
--v2-payload .context/cso-v2.payload.md \
--v3-payload .context/cso-v3.payload.md \
--output .context/cso-eval-matrix.json
bun scripts/cso-eval.ts materialize node-sql-injection vulnerable .context/cso-eval-app
```
The full matrix contains 960 cells: 40 pairs × two source variants × two modes ×
two skill versions × three repetitions. Each matched pair uses identical source,
model, host, and per-mode wall-clock budget. Daily runs have 600 seconds;
comprehensive runs have 1800 seconds. Each portable payload embeds the exact
generated `SKILL.md`, `sections/manifest.json`, and every generated section
listed by that manifest. The payload builder rejects missing or unlisted
generated sections and a mismatched major version. The matrix hashes the whole
payload, so a change to any section byte changes the pinned skill identity. The
commands above make no model calls and execute no fixture application.
Prepare consumable one-cell jobs and compile the generic producer runner:
```sh
bun build --compile \
--no-compile-autoload-dotenv \
--no-compile-autoload-bunfig \
--no-compile-autoload-tsconfig \
--no-compile-autoload-package-json \
scripts/cso-eval-producer.ts --outfile .context/cso-eval-producer
bun scripts/cso-eval.ts prepare .context/cso-eval-matrix.json \
--v2-payload .context/cso-v2.payload.md \
--v3-payload .context/cso-v3.payload.md \
--output .context/cso-eval-jobs
```
### Trusted five-artifact producer unit
The `.context/cso-eval-producer` command above creates a preparation artifact;
ordinary `bun run build` does not distribute the private producer. For a paid
run, start from one clean checkout and build a five-artifact staging unit in one
session:
```sh
stage="$(mktemp -d)"
bun run build:cso
bun build --compile \
--no-compile-autoload-dotenv \
--no-compile-autoload-bunfig \
--no-compile-autoload-tsconfig \
--no-compile-autoload-package-json \
scripts/cso-eval-producer.ts --outfile "$stage/cso-eval-producer"
install -m 0555 bin/gstack-cso-launcher bin/gstack-cso-core \
bin/gstack-cso-watchdog "$stage/"
install -m 0444 bin/.gstack-cso-generation "$stage/.gstack-cso-generation"
(cd "$stage" && (sha256sum cso-eval-producer gstack-cso-launcher \
gstack-cso-core gstack-cso-watchdog .gstack-cso-generation 2>/dev/null || \
shasum -a 256 cso-eval-producer gstack-cso-launcher gstack-cso-core \
gstack-cso-watchdog .gstack-cso-generation))
sudo install -d -o root -g root -m 0755 /opt/gstack-cso-producer
sudo install -o root -g root -m 0555 "$stage/cso-eval-producer" \
"$stage/gstack-cso-launcher" "$stage/gstack-cso-core" \
"$stage/gstack-cso-watchdog" /opt/gstack-cso-producer/
sudo install -o root -g root -m 0444 "$stage/.gstack-cso-generation" \
/opt/gstack-cso-producer/.gstack-cso-generation
```
Run `/opt/gstack-cso-producer/cso-eval-producer` as an unprivileged account.
Its startup check rejects missing, linked, writable, or non-root-owned members
and a writable or linked ancestor directory. Every receipt records the SHA-256
and byte length of all five artifacts in one installation identity; collection
rejects a batch if those identities differ. Preserve the printed hashes with
the release evidence and move or replace the complete unit together.
`prepare` creates a trusted `schedule.json` and 960 independent directories.
It initializes each generated application as a one-commit Git repository. Each
job has exactly one source variant and one versioned, complete instruction
payload. Root-skill pointers resolve only to sections embedded in that payload;
the producer must never load CSO instructions from `~/.claude`, another host
install, this source checkout, or the other version's payload. The generic runner
is compiled separately and contains no corpus generator, fixed alternative,
case oracle, or schedule. These preparation commands still make zero model calls.
Run each job on a fresh producer filesystem that contains only that job, the
compiled runner, the authenticated provider CLI, and the reviewed executable
gstack helper/runtime files. Do not install or copy any CSO `SKILL.md`, carved
section, generated skill tree, schedule, this repository checkout,
sibling jobs, fixed alternatives, or evaluator code to that filesystem. The
producer runner reads its opaque control file into memory and deletes it before
the agent process starts, so the case identifier and vulnerable/fixed label are
not agent inputs. Copy the selected hash-named directory as the literal path
`/producer/job`; `/producer` must contain only `job`. Put the runner in a system
tool directory and use a separate receipt mount. The runner rejects the full
prepared batch layout, extra files beside `job`, a reused job with prior state,
and receipt paths under `/producer`. The dedicated VM/container must ensure
other host paths do not contain evaluator inputs; a local directory layout does
not constrain a tool-using agent's absolute filesystem access.
The receipt must be written outside the isolated producer root:
```sh
mkdir -m 700 /receipts
CSO_EVAL_PAID=1 /opt/gstack-cso-producer/cso-eval-producer run \
/producer/job/producer-input.json /receipts/CELL_ID.json --execute-paid
```
Both the environment variable and flag are required because this is the only
command in the workflow that makes a paid model call. It reuses the repository's
Claude, Codex, and Gemini provider adapters and starts a fresh CLI process for
each cell. The requested model, normalized effective model, identity source,
timeout, output, token counts, pricing-table cost estimate, tool-call count, and
latency are bound into a hashed receipt. `provider_reported` means the CLI
resolved a different concrete ID; `requested_pin` means the adapter returned
the exact requested ID or had to fall back to that exact CLI pin. Use a concrete
model ID rather than an alias. The current adapters return only completed runs,
so first-useful-result latency is explicitly unmeasured rather than copied from
total latency. If a job is interrupted after its input is consumed, restore it
from the trusted prepared copy; never synthesize a receipt.
After returning receipts to the trusted evaluator, collect them against the
matrix and schedule:
```sh
bun scripts/cso-eval.ts collect .context/cso-eval-matrix.json \
.context/cso-eval-jobs/schedule.json .context/cso-eval-receipts \
.context/cso-eval-producer-batch.json
```
Collection makes no model calls. It rejects unknown, duplicate, changed, or
wrong-input receipts and reports scheduled/submitted/missing denominators for
every v2/v3 and daily/comprehensive group. It rejects an effective-model mismatch
between matched v2/v3 cells, leaving the source receipts available for diagnosis
and a clean rerun. The collected batch stores a compact receipt index and hashes;
raw transcripts remain in the private receipt directory for adjudication. Cost
in this batch is clearly labeled a
pricing-table estimate; only a host-billed amount may populate `EvalResult`'s
host-cost field.
The producer host needs Git, the chosen authenticated CLI, and reviewed gstack
executables and catalogs without their skill source tree. Its CSO instruction
bytes come only from the cell payload, never the installed skill or installed
carved sections. Comprehensive cells additionally need a local Docker daemon,
the exact qualified helper/runtime catalog pair, public dependency inputs or
verified cache hits, and the authenticated out-of-process assertion witness. The runtime
image workflow currently emits native staging evidence with private accuracy,
held-out repair, canary, and watchdog gates still pending. Such evidence can
measure setup failures, but it cannot support a release-qualifying tested repair
until those gates and catalog promotion are complete.
## Private oracle boundary
`test/helpers/cso-eval-oracles.ts` belongs to the **trusted evaluator**. It contains
the legitimate controls, initial security assertions, alternate held-out
assertions, and non-vacuous repair judge. It is never copied into materialized
source. The fixed alternative, expected manifest, and this evaluator module must
remain unavailable to producing agents throughout their sessions.
A separate directory alone does not restrict a host agent's file access. The
evaluation host must enforce this boundary with scoped tools or an isolated
producer filesystem and test it with the `held-out-oracle-visibility` gate. If it
cannot do that, report the gate as `not_run` and do not claim held-out
qualification. Product execution containment does not automatically isolate the
hosted producing agent.
Only trusted runtime observations enter `judgeRepair`. Boot and legitimate
controls must pass on the original; the exact security assertion must fail for
the intended reason. The patched app must pass that assertion, all held-out
assertions, controls, and existing tests with unchanged verification inputs.
Independent root-cause/feature review is mandatory; boundary-replacing mocks or
disabled functionality fail certification. A producer's `tested` or
`runtime_tested` label is never an oracle result.
`collect` preserves producer claims and performance receipts; it does not turn
them into `EvalResult`. The independent evaluator must inspect the retained CSO
report/bundle, execute the private assertions, and write finding judgments and
workflow outcomes. This separation is deliberate: automatically copying agent
claims into the trusted result contract would fabricate verification.
## Scoring and release gates
```sh
bun scripts/cso-eval.ts score .context/cso-eval-matrix.json \
.context/cso-eval-producer-batch.json .context/trusted-cso-results.json \
.context/cso-qualification.json
```
The result contract is `EvalResult` in `scripts/cso-eval.ts`. The independent
evaluator supplies finding judgments, the matching `producerReceiptHash`, and
hashes of private evidence. The release scoring command requires a complete
producer batch, checks its integrity and effective-model parity, and rejects any
trusted result that is not bound to that cell's receipt. Recheck
success additionally requires a distinct current-source observation and its
source hash. Correct alternative patches need not match the reference fix's
text. Keep actual transcripts and reports in private evaluation state; do not
commit them beside the fixtures.
Each metric includes its numerator and denominator. Duplicate findings cannot
inflate true positives. Fixed variants contribute false positives. Missing and
setup-blocked supported scenarios remain misses in recall and workflow rates.
Hypotheses do not become supported discoveries; v2 legacy review evidence cannot
become v3 reproduced, runtime-tested, or tested evidence. Unknown usage remains
unknown, and wall-clock budgets are not represented as model-spend caps.
The scorer checks 95% daily precision, 80% comprehensive high/critical recall,
no aggregate high/critical recall regression against v2, all core cold starts,
zero falsely certified runtime-tested repairs, and a correct held-out repair in
every application stack. It also requires complete matched results, mandatory
reports, containment/canary receipts, and enforced oracle separation. Missing
evaluations return
`unmeasured` or `partial`, never passing release gates.
The free accounting tests use clearly labeled synthetic observations to test the
scorer. They are **not measured agent performance**. No paid comparison, fixture
cold-start qualification, or containment qualification is claimed by this corpus.
+686
View File
@@ -0,0 +1,686 @@
{
"schemaVersion": 1,
"version": "cso-v3-pairs-3",
"cases": [
{
"id": "node-sql-injection",
"stack": "node",
"family": "sql-injection",
"severity": "high",
"rootCause": "sql-injection",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "87267be77a89123b31f36c86c9a52e6eafbd266a313f3aca788a16fc47be7c65",
"fixed": "069fb74d1b471879b4151ee60de6dcae5abe6b86935a811b3160afc888091ee5"
}
},
{
"id": "node-command-injection",
"stack": "node",
"family": "command-injection",
"severity": "high",
"rootCause": "command-injection",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "4336fcd76b10ee6c36455288e14411c0ac2c62407e7ef22ae83cf957f77d17ed",
"fixed": "bcf6a044ca201dba861c4764696e800230d9b215644465cd7ed31479d218c12d"
}
},
{
"id": "node-path-traversal",
"stack": "node",
"family": "path-traversal",
"severity": "high",
"rootCause": "path-traversal",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "d69e959678cc3076202dbe9963ecde251cfece5744ee6718f4e82387efd5c994",
"fixed": "48ba7360137d0732b139dc6061640768cbfa1e31f896d5feb93258bb341a4e34"
}
},
{
"id": "node-ssrf",
"stack": "node",
"family": "ssrf",
"severity": "high",
"rootCause": "ssrf",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "dddf678fa3c900b2dd75953df953308c8e649eba1b061018075338163dba9d41",
"fixed": "c6b0a7d485fb0becae52f41e072d51096610d9c31c0cf790225350f40acb01b1"
}
},
{
"id": "node-object-authorization",
"stack": "node",
"family": "object-authorization",
"severity": "high",
"rootCause": "object-authorization",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "de4a888619db2d1992b2d46671f0b5fcde48903d6909cb23321a4019a574674a",
"fixed": "32c3ddaa88abe9286e1905f68ee3261aaf9a4a209964d7e014f622d25f05dc2c"
}
},
{
"id": "node-tenant-isolation",
"stack": "node",
"family": "tenant-isolation",
"severity": "high",
"rootCause": "tenant-isolation",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "fe9a035c0362f3513c7fb54d48acb5a835c84c424a9148be8a453f67dd1823fb",
"fixed": "6df2ee7243dcb8e624c988320d8df23814912ce0d3c2fb9282c50070f3f5b159"
}
},
{
"id": "node-html-injection",
"stack": "node",
"family": "html-injection",
"severity": "medium",
"rootCause": "html-injection",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "a373a32baef89b2d6ae97aac6c0a8aa56aa2d43c2f4f1d034e24249b272cf1da",
"fixed": "c3c114c86bd25b1c1f1b2766ad39cbe100f1c42edaa961314a20a7c64d5cf8a3"
}
},
{
"id": "node-open-redirect",
"stack": "node",
"family": "open-redirect",
"severity": "medium",
"rootCause": "open-redirect",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "12f0a273a9dc9960b7be8345794482274f7207f98b416c5211c0eb2e73633ba9",
"fixed": "1547a8c5cce7a47b054e8b0d9ce5a065703d0a686c2fea8be3d70016290cbe4d"
}
},
{
"id": "node-mass-assignment",
"stack": "node",
"family": "mass-assignment",
"severity": "high",
"rootCause": "mass-assignment",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "f0c6f6e3465069d9eba208a6ca899b1a6438d679937b89ebcd56c768d9cbd85b",
"fixed": "cb08b01645e2186adfae2bdb17bd3d41240cb389f9ddd65223e286d91ac72d05"
}
},
{
"id": "node-resource-exhaustion",
"stack": "node",
"family": "resource-exhaustion",
"severity": "high",
"rootCause": "resource-exhaustion",
"location": {
"path": "app.mjs",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "1671cb1dd6639c19aa687ec02f70190c39bbc1b7ccf436783560a173f9bb8d57",
"fixed": "a78998de27bd98b4a90600352f2e431f34c0cc7a8a4c9f5de4fc8ec1877bb9ba"
}
},
{
"id": "bun-sql-injection",
"stack": "bun",
"family": "sql-injection",
"severity": "high",
"rootCause": "sql-injection",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "6e55eb6286eb0cd7a550bc0e2057b2344af21d34e0e73ff950e12446a5e306bc",
"fixed": "b7e9df66ff955ebf4ed538798fbd8187e5929c1e4d3bd82c655e7513cb024269"
}
},
{
"id": "bun-command-injection",
"stack": "bun",
"family": "command-injection",
"severity": "high",
"rootCause": "command-injection",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "d4b222144463b15d360de50c675bbd4e01cd91b6fe97c090124e11406970bb0e",
"fixed": "0522b906f1460ebb514ec45c7d4261575bf8bccb67c3add14ec83bc5afce4e3c"
}
},
{
"id": "bun-path-traversal",
"stack": "bun",
"family": "path-traversal",
"severity": "high",
"rootCause": "path-traversal",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "8d9225578ad93d0bf243d23e23db719ddf77f486b25065ff8797c093e4d8529b",
"fixed": "380d546d31f4563edcecd8eeaed7c02aadb169a662776a9a4b026f7ea514468f"
}
},
{
"id": "bun-ssrf",
"stack": "bun",
"family": "ssrf",
"severity": "high",
"rootCause": "ssrf",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "80728866a613c4a3c1c5ffe468a098152f6e3632afa049905feb0ed286664f99",
"fixed": "05992952d8922605ac472d1a39e1aed9b058453eb95296b469d022c62b6d1114"
}
},
{
"id": "bun-object-authorization",
"stack": "bun",
"family": "object-authorization",
"severity": "high",
"rootCause": "object-authorization",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "2d72c60a1aa2b560f00d3f066bdc0947bc52978755de7323402ee2f75498f9a6",
"fixed": "b839b3f4f8354407eb6577f423d1651b40f7c3a68b079b04624b0b0d6de0294b"
}
},
{
"id": "bun-tenant-isolation",
"stack": "bun",
"family": "tenant-isolation",
"severity": "high",
"rootCause": "tenant-isolation",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "9d880c06e01294c8e785471165642a899a555c15e9409346946802f0189db53a",
"fixed": "dd1b4c7fb3cce974bfe998a5df337f6270f7675a97ce91ca9647067d29bd4368"
}
},
{
"id": "bun-html-injection",
"stack": "bun",
"family": "html-injection",
"severity": "medium",
"rootCause": "html-injection",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "280970e0c738f3f98f72388b2094b30a81ae8de24631aab9759c5c467eb04dce",
"fixed": "01d9ca12aeac526627c851609d034a35e57326b9f27e798853ad4338c8d6f0fe"
}
},
{
"id": "bun-open-redirect",
"stack": "bun",
"family": "open-redirect",
"severity": "medium",
"rootCause": "open-redirect",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "92e0f59a1a5cc9346dee8941ef218a82f739b5fb1ca06595a38ae4e3be3e5e50",
"fixed": "93da544141e869c13e5136b834f4ae59f0469d17b55bc5455e69c9b2efe0e144"
}
},
{
"id": "bun-mass-assignment",
"stack": "bun",
"family": "mass-assignment",
"severity": "high",
"rootCause": "mass-assignment",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "046ae2d46f8bc9a185a7384a1dac8902cd0d4b8f23447564b77a27354e2587eb",
"fixed": "79bb3ced148331d009b3e680c6c2040f6885e62873daedad0667fd2fd6173e1b"
}
},
{
"id": "bun-resource-exhaustion",
"stack": "bun",
"family": "resource-exhaustion",
"severity": "high",
"rootCause": "resource-exhaustion",
"location": {
"path": "app.ts",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "785c96f9a0ee7b2e8a07135dd46c43fdf28d06caa2e5380d8c6d2a469397cc2c",
"fixed": "d8ed22d72e5eb14c5e745b5d702377147b53a071de634597e4239435fd3a1c42"
}
},
{
"id": "python-sql-injection",
"stack": "python",
"family": "sql-injection",
"severity": "high",
"rootCause": "sql-injection",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "fc2cb6a773be0e191372c33014deed8837738fa48843fa99eb8ff63367f5675d",
"fixed": "07518c636f923497ed4d474b68e471216af0efb77bbd0751a3fdbc222055c71e"
}
},
{
"id": "python-command-injection",
"stack": "python",
"family": "command-injection",
"severity": "high",
"rootCause": "command-injection",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "4137304be1b38300a68202f943e41680a0897a75c89a8fc222781e39c22f22a6",
"fixed": "99bf0f2010d595321018e25428e76d408cf5efc6c96284dd7144c5802c259091"
}
},
{
"id": "python-path-traversal",
"stack": "python",
"family": "path-traversal",
"severity": "high",
"rootCause": "path-traversal",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "c261d74c7a569025259984448056928741ccb07383684f9fda5e51b9280a4d5c",
"fixed": "0fa6edad6b84f90efda50d1e49c584c8ca51ddb8ef9998034ad86b2bb7d81b76"
}
},
{
"id": "python-ssrf",
"stack": "python",
"family": "ssrf",
"severity": "high",
"rootCause": "ssrf",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "5b06615f38d6d43ea4900551155eabd5b8ba9f856d15f78e79fde70856177792",
"fixed": "814315a11b6b600c5e338822fc42fc9c4d6f66b88fa86de664ab0ccdeb91166e"
}
},
{
"id": "python-object-authorization",
"stack": "python",
"family": "object-authorization",
"severity": "high",
"rootCause": "object-authorization",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "a151cda5d96ba2e7e9b940ceb936e28070b680d572079ba5364ab56a993599da",
"fixed": "a8083c530ddb9fa4d0c0b0f6de4487ce0df7b811d2fb79c8cdbcdae0d8e3e2b4"
}
},
{
"id": "python-tenant-isolation",
"stack": "python",
"family": "tenant-isolation",
"severity": "high",
"rootCause": "tenant-isolation",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "d8cad33e13f914d34c97eb365f5d7fed4f63c0cb64546abf182f28b8050c03e0",
"fixed": "7ac649e25edcf30030f0ed3c22aa3bfacb38516ab435db4dee240c5d8e8c45a4"
}
},
{
"id": "python-html-injection",
"stack": "python",
"family": "html-injection",
"severity": "medium",
"rootCause": "html-injection",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "aa9ca878ed3c100845728ee70fb1e4254d1ee5ef76d655b3c17d1269c17b190b",
"fixed": "5a6cd758d4e007291eb01d922ef7994cc2f36371a7e96c99fcb4fabe643e3ce8"
}
},
{
"id": "python-open-redirect",
"stack": "python",
"family": "open-redirect",
"severity": "medium",
"rootCause": "open-redirect",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "0c052ebc5fba7f165ba4e173ca477911f741ed512bd152e2b43a1b526f815267",
"fixed": "2a3e70475e00d98fe6e2d6505d0ff71b9a9985f701e6a9fd081801d0162dbbd1"
}
},
{
"id": "python-mass-assignment",
"stack": "python",
"family": "mass-assignment",
"severity": "high",
"rootCause": "mass-assignment",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "74ffa12e965869af108f8cfbde9903a66d3a96fc76478ece6739a36712f97f69",
"fixed": "f4311765ac908b668ecfa3665d8023cf910e6afb1aff799802036a9f212c75ff"
}
},
{
"id": "python-resource-exhaustion",
"stack": "python",
"family": "resource-exhaustion",
"severity": "high",
"rootCause": "resource-exhaustion",
"location": {
"path": "app.py",
"symbol": "action"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "50f4a5c5ad4ac5fd9fc0a935a37a0ede2d6735dbf31f7ed9fae9f63029d98181",
"fixed": "a25653768c0d551373d594a1505510325a01e06536fd1158c7b8617084fdb2dc"
}
},
{
"id": "rails-sql-injection",
"stack": "rails",
"family": "sql-injection",
"severity": "high",
"rootCause": "sql-injection",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "ebcfded5f3a00b76f803986cf1e82ca725df8ecd5268758ffe5c7b1ffed602e6",
"fixed": "3905e9acf8f564fb3ead4e53c823fca10a2bc6e28212b90cad95bf4cff515c7d"
}
},
{
"id": "rails-command-injection",
"stack": "rails",
"family": "command-injection",
"severity": "high",
"rootCause": "command-injection",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "7938f3f18bbd5effddbd28029476f305a443148351707142267411d56d5fb0b8",
"fixed": "a88dcb4e5d0bc19d79577791bbe4eccc946f22f7ad66a0471af005d83106bd5d"
}
},
{
"id": "rails-path-traversal",
"stack": "rails",
"family": "path-traversal",
"severity": "high",
"rootCause": "path-traversal",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "b49d633543845557b4dea97674b04542df2309d53c46c61dad6532b561b4179e",
"fixed": "a77ce178798cb01abaa55a815da3f8f16ab95add4103d540b27775735d857d9e"
}
},
{
"id": "rails-ssrf",
"stack": "rails",
"family": "ssrf",
"severity": "high",
"rootCause": "ssrf",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "e8fbb44ae9e01e44615dc991920c655ab97ad16c3ef8d425f9d94a3d215beedd",
"fixed": "0c2d952137fa644f4eba215b43988ad0092a4ce902863196309e65b30438661d"
}
},
{
"id": "rails-object-authorization",
"stack": "rails",
"family": "object-authorization",
"severity": "high",
"rootCause": "object-authorization",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "5378c421c287d6a79f1d7c4836e472fe0851a7436aaee431e23b317a7fc259ca",
"fixed": "976c0a27c7c647ddbd5e31d5a36668deb6e1ca5851702bbb280a2d1f81a3d84e"
}
},
{
"id": "rails-tenant-isolation",
"stack": "rails",
"family": "tenant-isolation",
"severity": "high",
"rootCause": "tenant-isolation",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "0a082cf0146c15cb4ca585b50a024e4596642dbb6462c3370b5452a8c25eb550",
"fixed": "5814fae616945cb1ead1d7046c89281c0f7215ccf4cb298a4cc517297adc0ba8"
}
},
{
"id": "rails-html-injection",
"stack": "rails",
"family": "html-injection",
"severity": "medium",
"rootCause": "html-injection",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "118599300873d0a10f3e920157707f71703cd58cae9d597e5c98cf046b410507",
"fixed": "12a2d8d6db8c6ed60faa1658aa63df414d60c6b85dcd7e417d229e7017a05949"
}
},
{
"id": "rails-open-redirect",
"stack": "rails",
"family": "open-redirect",
"severity": "medium",
"rootCause": "open-redirect",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "77cce00579bc9b10bfc0be2755b5b6446034585456799e4e3771740a0d7f59ab",
"fixed": "113b5e5c346fd4aebab9b4e3242d14784ee70e71c6e938c289e46dfcb968c4a8"
}
},
{
"id": "rails-mass-assignment",
"stack": "rails",
"family": "mass-assignment",
"severity": "high",
"rootCause": "mass-assignment",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "17fce4f956eaa654b1b90f6fe78ccc604accc820f9496517296800ca541bc436",
"fixed": "58b5132f336f6e115ff38b3c7b28bc0a00fca2444669008e3b30c3f6037a915e"
}
},
{
"id": "rails-resource-exhaustion",
"stack": "rails",
"family": "resource-exhaustion",
"severity": "high",
"rootCause": "resource-exhaustion",
"location": {
"path": "app/controllers/cases_controller.rb",
"symbol": "CasesController#show"
},
"coreColdStart": true,
"heldOut": true,
"filesHash": {
"vulnerable": "08e656bee75b724ba5b958c1887589df9931babec2c742e3b644236a677e007c",
"fixed": "fb7f5a5011d05e1f9d27d19c70aca5f64c11bd2cbc68d3396053e7169d8e94ec"
}
}
]
}
+323
View File
@@ -0,0 +1,323 @@
/** Immutable benchmark source generator. Expected outcomes live outside producer inputs. */
import { createHash } from 'node:crypto';
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
export const CORPUS_VERSION = 'cso-v3-pairs-3';
export const STACKS = ['node', 'bun', 'python', 'rails'] as const;
export const FAMILIES = ['sql-injection', 'command-injection', 'path-traversal', 'ssrf', 'object-authorization', 'tenant-isolation', 'html-injection', 'open-redirect', 'mass-assignment', 'resource-exhaustion'] as const;
export type EvalStack = typeof STACKS[number];
export type EvalFamily = typeof FAMILIES[number];
export type EvalVariant = 'vulnerable' | 'fixed';
export interface CorpusCase {
id: string; stack: EvalStack; family: EvalFamily; severity: 'critical' | 'high' | 'medium';
rootCause: string; location: { path: string; symbol: string };
coreColdStart: true; heldOut: true;
filesHash: { vulnerable: string; fixed: string };
}
export interface CorpusManifest { schemaVersion: 1; version: string; cases: CorpusCase[] }
const hash = (input: string) => createHash('sha256').update(input).digest('hex');
export function sourceHash(files: Record<string, string>): string {
return hash(JSON.stringify(Object.keys(files).sort().map(path => [path, hash(files[path])])));
}
function stackCase(id: string): { stack: EvalStack; family: EvalFamily } {
for (const stack of STACKS) for (const family of FAMILIES) if (id === `${stack}-${family}`) return { stack, family };
throw new Error('UNKNOWN_CORPUS_CASE');
}
const description: Record<EvalFamily, string> = {
'sql-injection': 'Look up the note belonging to an exact customer name.',
'command-injection': 'Format a display name using the system formatting utility. Display names contain letters, digits, underscores, and hyphens.',
'path-traversal': 'Read a file from the public documents directory.',
ssrf: 'Load the public status document from the internal status service.',
'object-authorization': 'Read an invoice belonging to the authenticated member.',
'tenant-isolation': 'List documents in the authenticated member\'s tenant.',
'html-injection': 'Render a welcome page containing a visitor-supplied display name as text.',
'open-redirect': 'Redirect a signed-in member to an application-relative path.',
'mass-assignment': 'Update the authenticated member\'s display name. Their role is managed by an administrator.',
'resource-exhaustion': 'Generate up to 100 preview records per request.',
};
interface PublicControl {
method: 'GET' | 'POST';
path: string;
body?: Record<string, string>;
status: number;
bodyIncludes: string;
location?: string;
}
/**
* Producer-visible regression controls exercise the feature's intended use.
* Attack payloads and alternate assertions stay in cso-eval-oracles.ts.
*/
function publicControl(family: EvalFamily): PublicControl {
const controls: Record<EvalFamily, PublicControl> = {
'sql-injection': { method: 'GET', path: '/action?name=alice', status: 200, bodyIncludes: 'HELLO_ALICE' },
'command-injection': { method: 'GET', path: '/action?name=Ada', status: 200, bodyIncludes: 'Ada' },
'path-traversal': { method: 'GET', path: '/action?file=hello.txt', status: 200, bodyIncludes: 'PUBLIC_HELLO' },
ssrf: { method: 'GET', path: '/action?url=http%3A%2F%2F127.0.0.1%3A8001%2Fpublic', status: 200, bodyIncludes: 'PUBLIC_STATUS' },
'object-authorization': { method: 'GET', path: '/action?id=invoice-1', status: 200, bodyIncludes: 'member-1' },
'tenant-isolation': { method: 'GET', path: '/action?tenant=tenant-a', status: 200, bodyIncludes: 'OWN_TENANT_DOCUMENT' },
'html-injection': { method: 'GET', path: '/action?name=Alice', status: 200, bodyIncludes: '<main>Welcome Alice</main>' },
'open-redirect': { method: 'GET', path: '/action?next=%2Faccount', status: 302, bodyIncludes: '/account', location: '/account' },
'mass-assignment': { method: 'POST', path: '/action', body: { display_name: 'Ada' }, status: 200, bodyIncludes: 'Ada' },
'resource-exhaustion': { method: 'GET', path: '/action?count=5', status: 200, bodyIncludes: 'count' },
};
return controls[family];
}
function javascriptControlTest(stack: 'node' | 'bun', family: EvalFamily): string {
const control = publicControl(family);
const imports = stack === 'node'
? 'import test from "node:test";\nimport assert from "node:assert/strict";\nimport { spawn } from "node:child_process";'
: 'import { test, expect } from "bun:test";';
const start = stack === 'node'
? 'const child = spawn(process.execPath, ["app.mjs"], {stdio: "ignore"});'
: 'const child = Bun.spawn({cmd: [process.execPath, "--no-install", "app.ts"], stdout: "ignore", stderr: "ignore"});';
const stop = stack === 'node'
? 'child.kill("SIGTERM");\n if (child.exitCode === null) await new Promise(resolve => child.once("exit", resolve));'
: 'child.kill();\n await child.exited;';
const equal = stack === 'node'
? `assert.equal(response.status, ${control.status});\n assert.ok(body.includes(${JSON.stringify(control.bodyIncludes)}));${control.location ? `\n assert.equal(response.headers.get("location"), ${JSON.stringify(control.location)});` : ''}`
: `expect(response.status).toBe(${control.status});\n expect(body).toContain(${JSON.stringify(control.bodyIncludes)});${control.location ? `\n expect(response.headers.get("location")).toBe(${JSON.stringify(control.location)});` : ''}`;
const options = control.body
? `{method: "POST", redirect: "manual", headers: {"content-type": "application/json"}, body: ${JSON.stringify(JSON.stringify(control.body))}}`
: '{redirect: "manual"}';
return `${imports}
async function waitUntilReady() {
for (let attempt = 0; attempt < 100; attempt++) {
try { if ((await fetch("http://127.0.0.1:8000/health")).status === 200) return; } catch {}
await new Promise(resolve => setTimeout(resolve, 20));
}
throw new Error("application did not become ready");
}
test("the intended member workflow remains available", async () => {
${start}
try {
await waitUntilReady();
const response = await fetch(${JSON.stringify(`http://127.0.0.1:8000${control.path}`)}, ${options});
const body = await response.text();
${equal}
} finally { ${stop} }
});
`;
}
function pythonControlTest(family: EvalFamily): string {
const control = publicControl(family);
const body = control.body ? JSON.stringify(control.body) : '';
return `import http.client
import json
import subprocess
import sys
import time
import unittest
class IntendedWorkflowTest(unittest.TestCase):
def test_intended_member_workflow_remains_available(self):
child = subprocess.Popen([sys.executable, "-I", "app.py"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
try:
for _ in range(100):
try:
connection = http.client.HTTPConnection("127.0.0.1", 8000, timeout=1)
connection.request("GET", "/health")
if connection.getresponse().status == 200:
break
except OSError:
time.sleep(0.02)
else:
self.fail("application did not become ready")
connection = http.client.HTTPConnection("127.0.0.1", 8000, timeout=2)
payload = ${JSON.stringify(body)}
headers = {"content-type": "application/json"} if payload else {}
connection.request(${JSON.stringify(control.method)}, ${JSON.stringify(control.path)}, body=payload or None, headers=headers)
response = connection.getresponse()
response_body = response.read().decode()
self.assertEqual(response.status, ${control.status})
self.assertIn(${JSON.stringify(control.bodyIncludes)}, response_body)
${control.location ? ` self.assertEqual(response.getheader("location"), ${JSON.stringify(control.location)})\n` : ''} finally:
child.terminate()
try:
child.wait(timeout=2)
except subprocess.TimeoutExpired:
child.kill()
if __name__ == "__main__":
unittest.main()
`;
}
function railsControlTest(family: EvalFamily): string {
const control = publicControl(family);
const verb = control.method.toLowerCase();
const request = control.body
? `${verb} ${JSON.stringify(control.path)}, params: ${JSON.stringify(control.body)}`
: `${verb} ${JSON.stringify(control.path)}`;
return `require "test_helper"
class IntendedWorkflowTest < ActionDispatch::IntegrationTest
test "the intended member workflow remains available" do
${request}
assert_response ${control.status}
assert_includes response.body, ${JSON.stringify(control.bodyIncludes)}
${control.location ? ` assert_equal ${JSON.stringify(control.location)}, response.headers["Location"]\n` : ''} end
end
`;
}
function jsSource(stack: 'node' | 'bun', family: EvalFamily, fixed: boolean): string {
const imports = ['import { createServer } from "node:http";'];
let setup = '';
if (family === 'sql-injection') {
imports.push(stack === 'node' ? 'import { DatabaseSync } from "node:sqlite";' : 'import { Database } from "bun:sqlite";');
setup += `const db = new ${stack === 'node' ? 'DatabaseSync' : 'Database'}(":memory:");\ndb.exec("CREATE TABLE notes (name TEXT, note TEXT); INSERT INTO notes VALUES ('alice', 'HELLO_ALICE'), ('admin', 'SECRET_ADMIN_NOTE')");\nconst query = (sql, ...args) => db.${stack === 'node' ? 'prepare' : 'query'}(sql).all(...args);\n`;
}
if (family === 'command-injection') imports.push('import { execSync, execFileSync } from "node:child_process";');
if (family === 'path-traversal') imports.push('import { readFileSync } from "node:fs";', 'import { resolve, sep } from "node:path";');
if (family === 'ssrf') setup += `createServer((request, response) => { response.end(request.url === "/public" ? "PUBLIC_STATUS" : "INTERNAL_SERVICE_SECRET"); }).listen(8001, "127.0.0.1");\n`;
if (family === 'object-authorization') setup += 'const invoices = {"invoice-1": {owner: "member-1", total: 12}, "invoice-2": {owner: "member-2", total: 9800}};\n';
if (family === 'tenant-isolation') setup += 'const documents = [{tenant: "tenant-a", body: "OWN_TENANT_DOCUMENT"}, {tenant: "tenant-b", body: "OTHER_TENANT_SECRET"}];\n';
if (family === 'mass-assignment') setup += 'const account = {id: "member-1", display_name: "Alice", role: "member"};\n';
const actions: Record<EvalFamily, string> = {
'sql-injection': fixed
? 'return send(200, query("SELECT note FROM notes WHERE name = ?", params.get("name") ?? ""));'
: 'return send(200, query("SELECT note FROM notes WHERE name = \'" + (params.get("name") ?? "") + "\'"));',
'command-injection': fixed
? 'const name = params.get("name") ?? "";\n if (!/^[A-Za-z0-9_-]{1,60}$/.test(name)) return send(400, {error: "invalid display name"});\n return send(200, {output: execFileSync("/usr/bin/printf", ["%s", name], {encoding: "utf8"})});'
: 'const name = params.get("name") ?? "";\n return send(200, {output: execSync(\'printf "%s" "\' + name + \'"\', {encoding: "utf8"})});',
'path-traversal': fixed
? 'const base = resolve("public");\n const file = resolve(base, params.get("file") ?? "hello.txt");\n if (!file.startsWith(base + sep)) return send(403, {error: "outside public documents"});\n return send(200, {document: readFileSync(file, "utf8")});'
: 'const file = resolve("public", params.get("file") ?? "hello.txt");\n return send(200, {document: readFileSync(file, "utf8")});',
ssrf: `${fixed ? 'const target = new URL(params.get("url") ?? "http://127.0.0.1:8001/public");\n if (target.origin !== "http://127.0.0.1:8001" || target.pathname !== "/public" || target.username || target.password || target.search || target.hash) return send(403, {error: "unsupported status source"});\n const result = await fetch(target, {redirect: "error"});' : 'const result = await fetch(params.get("url") ?? "http://127.0.0.1:8001/public");'}\n return send(200, {status: await result.text()});`,
'object-authorization': `const invoice = invoices[params.get("id") ?? "invoice-1"];\n if (!invoice) return send(404, {error: "not found"});\n ${fixed ? 'if (invoice.owner !== actor.id) return send(403, {error: "forbidden"});\n ' : ''}return send(200, invoice);`,
'tenant-isolation': `return send(200, documents.filter(document => document.tenant === ${fixed ? 'actor.tenant' : '(params.get("tenant") ?? actor.tenant)'}));`,
'html-injection': `const name = params.get("name") ?? "Alice";\n ${fixed ? 'const safe = name.replace(/[&<>"\']/g, char => ({"&": "&amp;", "<": "&lt;", ">": "&gt;", \'"\': "&quot;", "\'": "&#39;"}[char]));' : 'const safe = name;'}\n return send(200, "<main>Welcome " + safe + "</main>", "text/html");`,
'open-redirect': `const target = params.get("next") ?? "/account";\n ${fixed ? 'if (!/^\\/(?!\\/)/.test(target) || /[\\\\\\r\\n]/.test(target)) return send(400, {error: "invalid redirect"});\n ' : ''}response.setHeader("Location", target);\n return send(302, {next: target});`,
'mass-assignment': `${fixed ? 'if (typeof body.display_name === "string") account.display_name = body.display_name;' : 'Object.assign(account, body);'}\n return send(200, account);`,
'resource-exhaustion': `const count = Number(params.get("count") ?? "5");\n ${fixed ? 'if (!Number.isInteger(count) || count < 1 || count > 100) return send(400, {error: "preview count must be 1-100"});\n ' : ''}const previews = Array.from({length: count}, (_, index) => "preview-" + index);\n return send(200, {count: previews.length});`,
};
return `${imports.join('\n')}\n${setup}// Authentication middleware has established this member before the endpoint.\nconst actor = Object.freeze({id: "member-1", tenant: "tenant-a", role: "member"});\nasync function action(params, body, response, send) {\n ${actions[family]}\n}\ncreateServer(async (request, response) => {\n const send = (status, value, type = "application/json") => {response.writeHead(status, {"content-type": type}); response.end(type === "application/json" ? JSON.stringify(value) : value);};\n const url = new URL(request.url, "http://127.0.0.1:8000");\n if (url.pathname === "/health") return send(200, {ready: true});\n if (url.pathname !== "/action") return send(404, {error: "not found"});\n try {\n let raw = "";\n for await (const part of request) {raw += part; if (raw.length > 16384) return send(413, {error: "body too large"});}\n await action(url.searchParams, raw ? JSON.parse(raw) : {}, response, send);\n } catch {send(400, {error: "invalid request"});}\n}).listen(8000, "127.0.0.1");\n`;
}
function pythonSource(family: EvalFamily, fixed: boolean): string {
const setup: Partial<Record<EvalFamily, string>> = {
'sql-injection': `db = sqlite3.connect(":memory:", check_same_thread=False)\ndb.executescript("CREATE TABLE notes (name TEXT, note TEXT); INSERT INTO notes VALUES ('alice', 'HELLO_ALICE'), ('admin', 'SECRET_ADMIN_NOTE')")`,
'object-authorization': 'invoices = {"invoice-1": {"owner": "member-1", "total": 12}, "invoice-2": {"owner": "member-2", "total": 9800}}',
'tenant-isolation': 'documents = [{"tenant": "tenant-a", "body": "OWN_TENANT_DOCUMENT"}, {"tenant": "tenant-b", "body": "OTHER_TENANT_SECRET"}]',
'mass-assignment': 'account = {"id": "member-1", "display_name": "Alice", "role": "member"}',
ssrf: `class StatusService(BaseHTTPRequestHandler):\n def do_GET(self):\n self.send_response(200)\n self.end_headers()\n self.wfile.write(b"PUBLIC_STATUS" if self.path == "/public" else b"INTERNAL_SERVICE_SECRET")\n def log_message(self, *args):\n pass\nthreading.Thread(target=ThreadingHTTPServer(("127.0.0.1", 8001), StatusService).serve_forever, daemon=True).start()`,
};
const actions: Record<EvalFamily, string> = {
'sql-injection': `rows = db.execute(${fixed ? '"SELECT note FROM notes WHERE name = ?", (params.get("name", ""),)' : '"SELECT note FROM notes WHERE name = \'" + params.get("name", "") + "\'"'}).fetchall()\n return 200, [{"note": row[0]} for row in rows]`,
'command-injection': `name = params.get("name", "")\n ${fixed ? 'if not re.fullmatch(r"[A-Za-z0-9_-]{1,60}", name):\n return 400, {"error": "invalid display name"}\n output = subprocess.check_output(["/usr/bin/printf", "%s", name], text=True)' : 'output = subprocess.check_output(\'printf "%s" "\' + name + \'"\', shell=True, text=True)'}\n return 200, {"output": output}`,
'path-traversal': `base = pathlib.Path("public").resolve()\n path = (base / params.get("file", "hello.txt")).resolve()\n ${fixed ? 'if base not in path.parents:\n return 403, {"error": "outside public documents"}\n ' : ''}return 200, {"document": path.read_text()}`,
ssrf: `target = params.get("url", "http://127.0.0.1:8001/public")\n ${fixed ? 'parsed = urllib.parse.urlsplit(target)\n if parsed.scheme != "http" or parsed.netloc != "127.0.0.1:8001" or parsed.path != "/public" or parsed.query or parsed.fragment:\n return 403, {"error": "unsupported status source"}\n ' : ''}with urllib.request.urlopen(target, timeout=2) as result:\n return 200, {"status": result.read(16384).decode()}`,
'object-authorization': `invoice = invoices.get(params.get("id", "invoice-1"))\n if invoice is None:\n return 404, {"error": "not found"}\n ${fixed ? 'if invoice["owner"] != actor["id"]:\n return 403, {"error": "forbidden"}\n ' : ''}return 200, invoice`,
'tenant-isolation': `return 200, [document for document in documents if document["tenant"] == ${fixed ? 'actor["tenant"]' : 'params.get("tenant", actor["tenant"])'}]`,
'html-injection': `name = params.get("name", "Alice")\n return 200, "<main>Welcome " + ${fixed ? 'html.escape(name, quote=True)' : 'name'} + "</main>"`,
'open-redirect': `target = params.get("next", "/account")\n ${fixed ? 'if not target.startswith("/") or target.startswith("//") or any(char in target for char in "\\\\\\r\\n"):\n return 400, {"error": "invalid redirect"}\n ' : ''}return 302, {"next": target}`,
'mass-assignment': `${fixed ? 'if isinstance(body.get("display_name"), str):\n account["display_name"] = body["display_name"]' : 'account.update(body)'}\n return 200, account`,
'resource-exhaustion': `count = int(params.get("count", "5"))\n ${fixed ? 'if count < 1 or count > 100:\n return 400, {"error": "preview count must be 1-100"}\n ' : ''}previews = ["preview-" + str(index) for index in range(count)]\n return 200, {"count": len(previews)}`,
};
return `import html\nimport json\nimport pathlib\nimport re\nimport sqlite3\nimport subprocess\nimport threading\nimport urllib.parse\nimport urllib.request\nfrom http.server import BaseHTTPRequestHandler, ThreadingHTTPServer\n\n${setup[family] ?? ''}\n# Authentication middleware has established this member before the endpoint.\nactor = {"id": "member-1", "tenant": "tenant-a", "role": "member"}\n\ndef action(params, body):\n ${actions[family]}\n\nclass Application(BaseHTTPRequestHandler):\n def do_GET(self):\n self.dispatch()\n def do_POST(self):\n self.dispatch()\n def dispatch(self):\n url = urllib.parse.urlsplit(self.path)\n params = dict(urllib.parse.parse_qsl(url.query))\n try:\n size = int(self.headers.get("Content-Length", "0"))\n if size < 0 or size > 16384:\n status, value = 413, {"error": "body too large"}\n elif url.path == "/health":\n status, value = 200, {"ready": True}\n elif url.path != "/action":\n status, value = 404, {"error": "not found"}\n else:\n body = json.loads(self.rfile.read(size)) if size else {}\n status, value = action(params, body)\n except Exception:\n status, value = 400, {"error": "invalid request"}\n self.send_response(status)\n if status == 302:\n self.send_header("Location", value["next"])\n self.send_header("Content-Type", "text/html" if isinstance(value, str) else "application/json")\n self.end_headers()\n self.wfile.write((value if isinstance(value, str) else json.dumps(value)).encode())\n def log_message(self, *args):\n pass\n\nThreadingHTTPServer(("127.0.0.1", 8000), Application).serve_forever()\n`;
}
function railsSources(family: EvalFamily, fixed: boolean): Record<string, string> {
const setup: Partial<Record<EvalFamily, string>> = {
'sql-injection': '$database = SQLite3::Database.new(":memory:")\n$database.results_as_hash = true\n$database.execute_batch("CREATE TABLE notes (name TEXT, note TEXT); INSERT INTO notes VALUES (\'alice\', \'HELLO_ALICE\'), (\'admin\', \'SECRET_ADMIN_NOTE\')")',
'object-authorization': '$invoices = {"invoice-1" => {owner: "member-1", total: 12}, "invoice-2" => {owner: "member-2", total: 9800}}',
'tenant-isolation': '$documents = [{tenant: "tenant-a", body: "OWN_TENANT_DOCUMENT"}, {tenant: "tenant-b", body: "OTHER_TENANT_SECRET"}]',
'mass-assignment': '$account = {"id" => "member-1", "display_name" => "Alice", "role" => "member"}',
ssrf: `Thread.new do\n server = TCPServer.new("127.0.0.1", 8001)\n loop do\n client = server.accept\n request = client.gets.to_s\n while (line = client.gets) && line != "\\r\\n"; end\n body = request.start_with?("GET /public ") ? "PUBLIC_STATUS" : "INTERNAL_SERVICE_SECRET"\n client.write("HTTP/1.1 200 OK\\r\\nContent-Length: #{body.bytesize}\\r\\nConnection: close\\r\\n\\r\\n#{body}")\n client.close\n end\nend`,
};
const actions: Record<EvalFamily, string> = {
'sql-injection': `rows = $database.execute(${fixed ? '"SELECT note FROM notes WHERE name = ?", [params[:name].to_s]' : '"SELECT note FROM notes WHERE name = \'#{params[:name]}\'"'})\n render json: rows`,
'command-injection': `name = params[:name].to_s\n ${fixed ? 'return render(json: {error: "invalid display name"}, status: 400) unless /\\A[A-Za-z0-9_-]{1,60}\\z/.match?(name)\n output, = Open3.capture2("/usr/bin/printf", "%s", name)' : 'output, = Open3.capture2("sh", "-c", \'printf "%s" "\' + name + \'"\')'}\n render json: {output: output}`,
'path-traversal': `base = Rails.root.join("public").to_s\n path = File.expand_path(params[:file] || "hello.txt", base)\n ${fixed ? 'return render(json: {error: "outside public documents"}, status: 403) unless path.start_with?(base + File::SEPARATOR)\n ' : ''}render json: {document: File.read(path)}`,
ssrf: `target = URI.parse(params[:url] || "http://127.0.0.1:8001/public")\n ${fixed ? 'return render(json: {error: "unsupported status source"}, status: 403) unless target.scheme == "http" && target.host == "127.0.0.1" && target.port == 8001 && target.path == "/public" && !target.userinfo && !target.query && !target.fragment\n ' : ''}render json: {status: Net::HTTP.get(target)}`,
'object-authorization': `invoice = $invoices[params[:id] || "invoice-1"]\n return render(json: {error: "not found"}, status: 404) unless invoice\n ${fixed ? 'return render(json: {error: "forbidden"}, status: 403) unless invoice[:owner] == actor[:id]\n ' : ''}render json: invoice`,
'tenant-isolation': `render json: $documents.select { |document| document[:tenant] == ${fixed ? 'actor[:tenant]' : '(params[:tenant] || actor[:tenant])'} }`,
'html-injection': `name = params[:name] || "Alice"\n render html: ("<main>Welcome " + ${fixed ? 'ERB::Util.html_escape(name)' : 'name'} + "</main>").html_safe`,
'open-redirect': `target = params[:next] || "/account"\n ${fixed ? 'return render(json: {error: "invalid redirect"}, status: 400) unless target.start_with?("/") && !target.start_with?("//") && !/[\\\\\\r\\n]/.match?(target)\n ' : ''}response.set_header("Location", target)\n render json: {next: target}, status: 302`,
'mass-assignment': `$account.merge!(${fixed ? 'params.permit(:display_name).to_h' : 'params.permit!.to_h.except("controller", "action")'})\n render json: $account`,
'resource-exhaustion': `count = Integer(params[:count] || "5")\n ${fixed ? 'return render(json: {error: "preview count must be 1-100"}, status: 400) unless (1..100).cover?(count)\n ' : ''}previews = Array.new(count) { |index| "preview-#{index}" }\n render json: {count: previews.length}`,
};
return {
'Gemfile': 'source "https://rubygems.org"\ngem "rails", "= 8.1.2"\ngem "puma", "= 7.2.0"\ngem "sqlite3", "= 2.9.0"\n',
'Gemfile.lock': readFileSync(new URL('./rails.Gemfile.lock', import.meta.url), 'utf8'),
'config.ru': 'require_relative "config/environment"\nrun Rails.application\n',
'config/boot.rb': 'ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)\nrequire "bundler/setup"\n',
'config/application.rb': `require_relative "boot"\nrequire "rails"\nrequire "action_controller/railtie"\nrequire "sqlite3"\nrequire "open3"\nrequire "net/http"\nrequire "socket"\nrequire "erb"\nmodule MemberPortal\n class Application < Rails::Application\n config.load_defaults 8.1\n config.eager_load = false\n config.secret_key_base = "cso-synthetic-test-key-not-a-production-credential"\n config.hosts = ["127.0.0.1", "localhost"]\n config.action_controller.allow_forgery_protection = false if Rails.env.test?\n end\nend\n${setup[family] ?? ''}\n`,
'config/environment.rb': 'require_relative "application"\nRails.application.initialize!\n',
'config/routes.rb': 'Rails.application.routes.draw do\n match "/action", to: "cases#show", via: [:get, :post]\n get "/health", to: proc { [200, {"content-type" => "application/json"}, [\'{"ready":true}\']] }\nend\n',
'app/controllers/cases_controller.rb': `class CasesController < ActionController::Base\n def show\n # Authentication middleware has established this member before the endpoint.\n actor = {id: "member-1", tenant: "tenant-a", role: "member"}\n ${actions[family]}\n end\nend\n`,
'bin/rails': '#!/usr/bin/env ruby\nAPP_PATH = File.expand_path("../config/application", __dir__)\nrequire_relative "../config/boot"\nrequire "rails/commands"\n',
};
}
/** Only application files are returned. Neither oracle inputs nor expected labels are included. */
export function sourceFiles(id: string, variant: EvalVariant): Record<string, string> {
const { stack, family } = stackCase(id);
if (variant !== 'vulnerable' && variant !== 'fixed') throw new Error('INVALID_CORPUS_VARIANT');
const fixed = variant === 'fixed';
let files: Record<string, string>;
if (stack === 'node' || stack === 'bun') {
const node = stack === 'node';
files = {
[node ? 'app.mjs' : 'app.ts']: jsSource(stack, family, fixed),
[node ? 'test/control.test.mjs' : 'test/control.test.ts']: javascriptControlTest(stack, family),
'package.json': JSON.stringify({
name: 'member-portal', version: '1.0.0', private: true, type: 'module',
scripts: { start: node ? 'node app.mjs' : 'bun app.ts', test: node ? 'node --test' : 'bun test' },
}, null, 2) + '\n',
};
files[stack === 'node' ? 'package-lock.json' : 'bun.lock'] = JSON.stringify(stack === 'node'
? { name: 'member-portal', version: '1.0.0', lockfileVersion: 3, packages: { '': { name: 'member-portal', version: '1.0.0' } } }
: { lockfileVersion: 1, configVersion: 1, workspaces: { '': { name: 'member-portal' } }, packages: {} }, null, 2) + '\n';
} else if (stack === 'python') files = {
'app.py': pythonSource(family, fixed),
'test_control.py': pythonControlTest(family),
'requirements.txt': '# Uses only the pinned Python runtime standard library.\n',
};
else files = {
...railsSources(family, fixed),
'test/test_helper.rb': 'ENV["RAILS_ENV"] ||= "test"\nrequire_relative "../config/environment"\nrequire "rails/test_help"\n',
'test/integration/intended_workflow_test.rb': railsControlTest(family),
};
files['README.md'] = `# Member portal\n\n${description[family]}\n\nThe endpoint is /action. A test authentication adapter establishes member-1 in\ntenant-a with the member role before application logic. Authentication itself\nis outside this fixture's assessment scope. /health is a readiness endpoint.\n\nRun only through the CSO execution helper. This application is a security\nbenchmark and must not be exposed to a host network or deployed.\n`;
if (family === 'path-traversal') { files['public/hello.txt'] = 'PUBLIC_HELLO'; files['private.txt'] = 'PRIVATE_DOCUMENT_SECRET'; }
return files;
}
export function createManifest(): CorpusManifest {
return { schemaVersion: 1, version: CORPUS_VERSION, cases: STACKS.flatMap(stack => FAMILIES.map(family => {
const id = `${stack}-${family}`;
return { id, stack, family, severity: ['html-injection', 'open-redirect'].includes(family) ? 'medium' as const : 'high' as const,
rootCause: family, location: { path: stack === 'rails' ? 'app/controllers/cases_controller.rb' : stack === 'node' ? 'app.mjs' : stack === 'bun' ? 'app.ts' : 'app.py', symbol: stack === 'rails' ? 'CasesController#show' : 'action' },
coreColdStart: true as const, heldOut: true as const, filesHash: { vulnerable: sourceHash(sourceFiles(id, 'vulnerable')), fixed: sourceHash(sourceFiles(id, 'fixed')) } };
})) };
}
export function loadCorpusManifest(): CorpusManifest {
const manifest = JSON.parse(readFileSync(new URL('./manifest.json', import.meta.url), 'utf8')) as CorpusManifest;
if (JSON.stringify(manifest) !== JSON.stringify(createManifest())) throw new Error('CORPUS_INTEGRITY_MISMATCH');
return manifest;
}
export function materializeCase(id: string, variant: EvalVariant, destination: string): { path: string; sourceHash: string } {
const manifest = loadCorpusManifest();
const spec = manifest.cases.find(item => item.id === id);
if (!spec) throw new Error('UNKNOWN_CORPUS_CASE');
const path = resolve(destination);
if (existsSync(path)) throw new Error('CORPUS_DESTINATION_EXISTS');
const parent = dirname(path);
if (realpathSync(parent) !== parent || !lstatSync(parent).isDirectory()) throw new Error('UNSAFE_CORPUS_DESTINATION');
const files = sourceFiles(id, variant);
if (sourceHash(files) !== spec.filesHash[variant]) throw new Error('CORPUS_INTEGRITY_MISMATCH');
mkdirSync(path, { mode: 0o700 });
for (const [file, contents] of Object.entries(files)) {
const output = join(path, file); mkdirSync(dirname(output), { recursive: true, mode: 0o700 });
writeFileSync(output, contents, { flag: 'wx', mode: 0o600 });
}
return { path, sourceHash: spec.filesHash[variant] };
}
+488
View File
@@ -0,0 +1,488 @@
{
"schemaVersion": 1,
"generatedAt": "2026-09-11",
"ruby": "3.2.8",
"bundler": "2.6.7",
"command": "BUNDLE_FORCE_RUBY_PLATFORM=true BUNDLE_IGNORE_CONFIG=true bundle _2.6.7_ update --bundler=2.6.7",
"gemfileSha256": "f09fb9778d39ae776be3c559f7c49402cfa82b06853939e558bda81dde0dfc6c",
"lockSha256": "ab7c4cbb2f6f81a04cd400467bfd78cf7e7aa75fc600530d2646986f7ffaf139",
"archives": [
{
"name": "action_text-trix",
"version": "2.1.19",
"sha256": "7012f59421009cf284aa651294896414d653a61a2417c9b8714c8476d2f74009",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "actioncable",
"version": "8.1.2",
"sha256": "dc31efc34cca9cdefc5c691ddb8b4b214c0ea5cd1372108cbc1377767fb91969",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "actionmailbox",
"version": "8.1.2",
"sha256": "058b2fb1980e5d5a894f675475fcfa45c62631103d5a2596d9610ec81581889b",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "actionmailer",
"version": "8.1.2",
"sha256": "f4c1d2060f653bfe908aa7fdc5a61c0e5279670de992146582f2e36f8b9175e9",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "actionpack",
"version": "8.1.2",
"sha256": "ced74147a1f0daafaa4bab7f677513fd4d3add574c7839958f7b4f1de44f8423",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "actiontext",
"version": "8.1.2",
"sha256": "0bf57da22a9c19d970779c3ce24a56be31b51c7640f2763ec64aa72e358d2d2d",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "actionview",
"version": "8.1.2",
"sha256": "80455b2588911c9b72cec22d240edacb7c150e800ef2234821269b2b2c3e2e5b",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "activejob",
"version": "8.1.2",
"sha256": "908dab3713b101859536375819f4156b07bdf4c232cc645e7538adb9e302f825",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "activemodel",
"version": "8.1.2",
"sha256": "e21358c11ce68aed3f9838b7e464977bc007b4446c6e4059781e1d5c03bcf33e",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "activerecord",
"version": "8.1.2",
"sha256": "acfbe0cadfcc50fa208011fe6f4eb01cae682ebae0ef57145ba45380c74bcc44",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "activestorage",
"version": "8.1.2",
"sha256": "8a63a48c3999caeee26a59441f813f94681fc35cc41aba7ce1f836add04fba76",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "activesupport",
"version": "8.1.2",
"sha256": "88842578ccd0d40f658289b0e8c842acfe9af751afee2e0744a7873f50b6fdae",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "base64",
"version": "0.3.0",
"sha256": "27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "bigdecimal",
"version": "4.1.2",
"sha256": "ccc836eab720a525529f70ed0de26a206fdbc9a9e8ac67b3b4ac7318b03e114d",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "builder",
"version": "3.3.0",
"sha256": "497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "concurrent-ruby",
"version": "1.3.8",
"sha256": "b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "connection_pool",
"version": "3.0.2",
"sha256": "33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "crass",
"version": "1.0.7",
"sha256": "94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "date",
"version": "3.5.1",
"sha256": "12e09477dc932afe45bf768cd362bf73026804e0db1e6c314186d6cd0bee3344",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "drb",
"version": "2.2.3",
"sha256": "0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "erb",
"version": "6.0.7",
"sha256": "ef8339f928aa33be9205534be19e9c0daf310c4cc4eb85fd409141c094c57d61",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "erubi",
"version": "1.13.1",
"sha256": "a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "globalid",
"version": "1.4.0",
"sha256": "037f12fbf1d9d7a014d501c2d5c77356fd4ddd96d7a7991d6700bba96706f427",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "i18n",
"version": "1.15.2",
"sha256": "00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "io-console",
"version": "0.9.2",
"sha256": "efa74f891dd03c0939a931dfc6e74c2813d904763d456ea9762b0525e748db08",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "irb",
"version": "1.18.0",
"sha256": "de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "json",
"version": "3.0.2",
"sha256": "2afafb9c82faabfb60ed2f37707973fdcb766dba2c99f0b9ed85f073d1d4c3d0",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "logger",
"version": "1.7.0",
"sha256": "196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "loofah",
"version": "2.25.2",
"sha256": "2007f746959ac65552456e04b433e83deb22759ab38c838b4445c70e43425918",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "mail",
"version": "2.9.1",
"sha256": "06574eca475253d6c18145dd70af80d0eb970182d55053497c5f4d797ea160e8",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "marcel",
"version": "1.2.1",
"sha256": "1678e9360e32f9eafa917c80029e2f6d10b2715c66a4b87b6d0da9b9cd1f859f",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "mini_mime",
"version": "1.1.5",
"sha256": "8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "mini_portile2",
"version": "2.8.9",
"sha256": "0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "minitest",
"version": "6.0.6",
"sha256": "153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "net-imap",
"version": "0.6.6",
"sha256": "96aa4ee50df3060203e649efc341f53480b791d49e150f2fdebf68beb141a8df",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "net-pop",
"version": "0.1.2",
"sha256": "848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "net-protocol",
"version": "0.3.0",
"sha256": "ba310c3d4f1cad46bb1ab20336b06669b1ff8f7c568d9cb9342b32a718547472",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "net-smtp",
"version": "0.5.1",
"sha256": "ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "nio4r",
"version": "2.7.5",
"sha256": "d14779d2a9b012ec0148a53344fbb2ed2a3c4d90c5dd923bf281135ab983b2c9",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "nokogiri",
"version": "1.19.4",
"sha256": "50c951611c92bca05c51411aef45f1cbc50f2821c4802758c5c6d34696533ab5",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "pp",
"version": "0.6.4",
"sha256": "dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "prettyprint",
"version": "0.2.0",
"sha256": "2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "prism",
"version": "1.9.0",
"sha256": "7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "puma",
"version": "7.2.0",
"sha256": "5ef97cc64c0579e6a507cded86286869b6387d58d28abe347c1dd1d7decdf6d0",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "racc",
"version": "1.8.1",
"sha256": "54f2e6d1e1b91c154013277d986f52a90e5ececbe91465d29172e49342732b98",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "rack",
"version": "3.2.7",
"sha256": "93e13e1c24f93556671d85d2d79fa228c3485815c50d7e2f265b5330c6528fb7",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "rack-session",
"version": "2.1.2",
"sha256": "595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "rack-test",
"version": "2.2.0",
"sha256": "005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "rackup",
"version": "2.3.1",
"sha256": "6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "rails",
"version": "8.1.2",
"sha256": "5069061b23dfa8706b9f0159ae8b9d35727359103178a26962b868a680ba7d95",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "rails-dom-testing",
"version": "2.3.0",
"sha256": "8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "rails-html-sanitizer",
"version": "1.7.1",
"sha256": "e797a7c9b01e567307e317c576b49ab4168017e63eea4dba9ce3cb587e2f22c2",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "railties",
"version": "8.1.2",
"sha256": "1289ece76b4f7668fc46d07e55cc992b5b8751f2ad85548b7da351b8c59f8055",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "rake",
"version": "13.4.2",
"sha256": "cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "rbs",
"version": "4.1.3",
"sha256": "193582897752597ee7cd2b9d6bf0a7014acd05490d86eb47769cdfd9f2d00ff5",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "rdoc",
"version": "8.0.0",
"sha256": "03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "reline",
"version": "0.7.0",
"sha256": "5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "securerandom",
"version": "0.4.1",
"sha256": "cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "sqlite3",
"version": "2.9.0",
"sha256": "ece9c00b32ec5f550d3a4a35c41ea8d738563589f090b9dfd0d510b7ae5f296c",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "thor",
"version": "1.5.0",
"sha256": "e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "timeout",
"version": "0.6.1",
"sha256": "78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "tsort",
"version": "0.2.0",
"sha256": "9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "tzinfo",
"version": "2.0.6",
"sha256": "8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "uri",
"version": "1.1.1",
"sha256": "379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "useragent",
"version": "0.16.11",
"sha256": "700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "websocket-driver",
"version": "0.8.2",
"sha256": "f60120d1377cccf24100bcd1ebc25ceea6f3d5a013fc8e9d16b721016200946e",
"source": "RubyGems version API",
"platform": "java"
},
{
"name": "websocket-extensions",
"version": "0.1.5",
"sha256": "1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241",
"source": "RubyGems version API",
"platform": "ruby"
},
{
"name": "zeitwerk",
"version": "2.8.3",
"sha256": "2c85125a8467ce069e20123d1e709a08955c9d29c118c25b46b7b7fafdbb92e5",
"source": "RubyGems version API",
"platform": "ruby"
}
],
"qualification": "not-run; metadata resolution only, no fixture application executed"
}
+210
View File
@@ -0,0 +1,210 @@
GEM
remote: https://rubygems.org/
specs:
action_text-trix (2.1.19)
railties
actioncable (8.1.2)
actionpack (= 8.1.2)
activesupport (= 8.1.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionmailbox (8.1.2)
actionpack (= 8.1.2)
activejob (= 8.1.2)
activerecord (= 8.1.2)
activestorage (= 8.1.2)
activesupport (= 8.1.2)
mail (>= 2.8.0)
actionmailer (8.1.2)
actionpack (= 8.1.2)
actionview (= 8.1.2)
activejob (= 8.1.2)
activesupport (= 8.1.2)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
actionpack (8.1.2)
actionview (= 8.1.2)
activesupport (= 8.1.2)
nokogiri (>= 1.8.5)
rack (>= 2.2.4)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (8.1.2)
action_text-trix (~> 2.1.15)
actionpack (= 8.1.2)
activerecord (= 8.1.2)
activestorage (= 8.1.2)
activesupport (= 8.1.2)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (8.1.2)
activesupport (= 8.1.2)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activejob (8.1.2)
activesupport (= 8.1.2)
globalid (>= 0.3.6)
activemodel (8.1.2)
activesupport (= 8.1.2)
activerecord (8.1.2)
activemodel (= 8.1.2)
activesupport (= 8.1.2)
timeout (>= 0.4.0)
activestorage (8.1.2)
actionpack (= 8.1.2)
activejob (= 8.1.2)
activerecord (= 8.1.2)
activesupport (= 8.1.2)
marcel (~> 1.0)
activesupport (8.1.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
json
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
base64 (0.3.0)
bigdecimal (4.1.2)
builder (3.3.0)
concurrent-ruby (1.3.8)
connection_pool (3.0.2)
crass (1.0.7)
date (3.5.1)
drb (2.2.3)
erb (6.0.7)
erubi (1.13.1)
globalid (1.4.0)
activesupport (>= 6.1)
i18n (1.15.2)
concurrent-ruby (~> 1.0)
io-console (0.9.2)
irb (1.18.0)
pp (>= 0.6.0)
prism (>= 1.3.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
json (3.0.2)
logger (1.7.0)
loofah (2.25.2)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.9.1)
logger
mini_mime (>= 0.1.1)
net-imap
net-pop
net-smtp
marcel (1.2.1)
mini_mime (1.1.5)
mini_portile2 (2.8.9)
minitest (6.0.6)
drb (~> 2.0)
prism (~> 1.5)
net-imap (0.6.6)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.3.0)
timeout
net-smtp (0.5.1)
net-protocol
nio4r (2.7.5)
nokogiri (1.19.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
pp (0.6.4)
prettyprint
prettyprint (0.2.0)
prism (1.9.0)
puma (7.2.0)
nio4r (~> 2.0)
racc (1.8.1)
rack (3.2.7)
rack-session (2.1.2)
base64 (>= 0.1.0)
rack (>= 3.0.0)
rack-test (2.2.0)
rack (>= 1.3)
rackup (2.3.1)
rack (>= 3)
rails (8.1.2)
actioncable (= 8.1.2)
actionmailbox (= 8.1.2)
actionmailer (= 8.1.2)
actionpack (= 8.1.2)
actiontext (= 8.1.2)
actionview (= 8.1.2)
activejob (= 8.1.2)
activemodel (= 8.1.2)
activerecord (= 8.1.2)
activestorage (= 8.1.2)
activesupport (= 8.1.2)
bundler (>= 1.15.0)
railties (= 8.1.2)
rails-dom-testing (2.3.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.7.1)
loofah (~> 2.25, >= 2.25.2)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
railties (8.1.2)
actionpack (= 8.1.2)
activesupport (= 8.1.2)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0, >= 1.2.2)
tsort (>= 0.2)
zeitwerk (~> 2.6)
rake (13.4.2)
rbs (4.1.3)
logger
prism (>= 1.6.0)
tsort
rdoc (8.0.0)
erb
prism (>= 1.6.0)
rbs (>= 4.0.0)
tsort
reline (0.7.0)
io-console (~> 0.5)
securerandom (0.4.1)
sqlite3 (2.9.0)
mini_portile2 (~> 2.8.0)
thor (1.5.0)
timeout (0.6.1)
tsort (0.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uri (1.1.1)
useragent (0.16.11)
websocket-driver (0.8.2)
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
zeitwerk (2.8.3)
PLATFORMS
ruby
DEPENDENCIES
puma (= 7.2.0)
rails (= 8.1.2)
sqlite3 (= 2.9.0)
BUNDLED WITH
2.6.7
+41
View File
@@ -0,0 +1,41 @@
/* Trusted, dependency-free loopback service for runtime/verifier smoke tests.
* This is infrastructure evidence, not a vulnerable/fixed evaluation pair. */
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
int main(void) {
signal(SIGPIPE, SIG_IGN);
int server = socket(AF_INET, SOCK_STREAM, 0), reuse = 1;
struct sockaddr_in address = {.sin_family = AF_INET, .sin_port = htons(34568),
.sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
if (server < 0 || setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) ||
bind(server, (void *)&address, sizeof(address)) || listen(server, 8)) return 2;
for (;;) {
int client = accept(server, NULL, NULL);
if (client < 0) continue;
char request[2048] = {0}, response[256];
ssize_t received = read(client, request, sizeof(request) - 1);
const char *status = "404 Not Found", *body = "MISSING";
if (received > 0 && strncmp(request, "GET /control ", 13) == 0) {
status = "200 OK"; body = "CONTROL_OK";
} else if (received > 0 && strncmp(request, "GET /security ", 14) == 0) {
status = "403 Forbidden"; body = "DENIED";
}
int length = snprintf(response, sizeof(response),
"HTTP/1.1 %s\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n%s",
status, strlen(body), body);
if (length > 0 && (size_t)length < sizeof(response)) {
size_t sent = 0;
while (sent < (size_t)length) {
ssize_t count = write(client, response + sent, (size_t)length - sent);
if (count <= 0) break;
sent += (size_t)count;
}
}
close(client);
}
}
+4
View File
@@ -21,6 +21,10 @@ afterEach(() => {
});
describe('atomicWriteSync', () => {
test('publishes a no-replace artifact once', () => {
const target=path.join(dir,'immutable.json');atomicWriteSync(target,'first',{mode:0o600,noReplace:true});
expect(()=>atomicWriteSync(target,'second',{mode:0o600,noReplace:true})).toThrow();expect(fs.readFileSync(target,'utf8')).toBe('first');
});
test('writes the content and leaves no tmp file behind', () => {
const target = path.join(dir, 'out.json');
atomicWriteSync(target, '{"a":1}');
+29 -3
View File
@@ -430,9 +430,10 @@ describe('gen-skill-docs', () => {
});
test('tier 2+ skills contain ELI10 simplification rules (AskUserQuestion format)', () => {
// Root SKILL.md is tier 1 (no AskUserQuestion format). Check a tier 2+ skill instead.
// Root SKILL.md is tier 1 and CSO intentionally uses a private startup with
// no shared PREAMBLE. Check a regular tier 2+ PREAMBLE consumer instead.
// v1.7.0.0 Pros/Cons format uses "ELI10 (ALWAYS)" rather than "Simplify (ELI10".
const content = fs.readFileSync(path.join(ROOT, 'cso', 'SKILL.md'), 'utf-8');
const content = fs.readFileSync(path.join(ROOT, 'review', 'SKILL.md'), 'utf-8');
expect(content).toContain('ELI10');
expect(content).toContain('plain English');
expect(content).toContain('not function names');
@@ -3616,7 +3617,9 @@ describe('LEARNINGS_LOG resolver', () => {
});
describe('CONFIDENCE_CALIBRATION resolver', () => {
const CONFIDENCE_SKILLS = ['review', 'ship', 'plan-eng-review', 'cso'];
// CSO owns a distinct evidence rubric; the shared numerical confidence
// resolver would conflict with that contract and its private startup.
const CONFIDENCE_SKILLS = ['review', 'ship', 'plan-eng-review'];
for (const skill of CONFIDENCE_SKILLS) {
test(`${skill} generated SKILL.md contains confidence calibration`, () => {
@@ -3759,6 +3762,29 @@ describe('voice-triggers processing', () => {
expect(frontmatter).not.toContain('voice-triggers:');
});
test('generated Claude CSO skill preauthorizes only the trusted launcher', () => {
const expected = [
'Bash(~/.claude/skills/gstack/bin/gstack-cso-launcher *)',
'Bash(~/.claude/skills/gstack/bin/gstack-cso-launcher.exe *)',
];
for (const file of ['cso/SKILL.md.tmpl', 'cso/SKILL.md']) {
const content = fs.readFileSync(path.join(ROOT, file), 'utf-8');
const fmEnd = content.indexOf('\n---', 4);
const frontmatter = Bun.YAML.parse(content.slice(4, fmEnd)) as Record<string, unknown>;
expect(frontmatter['allowed-tools'], file).toEqual(expected);
}
});
test('generated CSO host variants retain challenge fallback and host-containment disclosure', () => {
const claude = fs.readFileSync(path.join(ROOT, 'cso', 'SKILL.md'), 'utf-8');
const codex = fs.readFileSync(path.join(EXTERNAL_OUT, '.agents', 'skills', 'gstack-cso', 'SKILL.md'), 'utf-8');
for (const content of [claude, codex]) {
expect(content).toContain('sequential challenge; independent agent unavailable');
expect(content).toContain('Containment does not sandbox the host agent or kernel.');
expect(content).toContain('Do not request broader tool access solely to obtain an independent reviewer.');
}
});
// Gen-time-only keys: interactive + benefits-from are read from the .tmpl by
// buildContext; the generated copy has no reader (the host reads name/
// description/allowed-tools/hooks; gbrain: is runtime-read and NOT stripped).
+12 -11
View File
@@ -363,7 +363,7 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
scenario:
'Run a security audit on this repository in --owasp mode (OWASP Top 10 only). Resolve the mode, do the Phase 0 stack detection and Phase 1 attack-surface census, then run the scoped audit phases and produce the findings report. Skip any step that needs network access.',
staticInvariants: {
// Dispatch + always-run + FP-filtering phases are ALWAYS loaded (security).
// Dispatch, trusted execution, evidence/proof, reporting and recovery stay always loaded.
mustStayInSkeleton: [
'## Arguments',
'## Mode Resolution',
@@ -372,6 +372,9 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
'### Phase 12',
'### Phase 13',
'### Phase 14',
'**Private startup.**',
'CSO evidence rubric',
'identical security assertion',
],
// Earliest-use: mode must be resolvable before any section is read (codex #6).
mustPrecedeStop: ['## Arguments', '## Mode Resolution'],
@@ -383,17 +386,15 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
],
gateAfterStop: undefined,
},
behavioral: 'prompt',
// +Conductor AUQ-default-prose rule + one-way/continuation safety in the
// always-loaded AskUserQuestion Format section.
// v1.2.0 activation lift: first-run-guidance section in the shared preamble.
maxSkeletonBytes: 61_800, // + v2.0 {{ASIDE_RESEARCH}} (Aside first, WebSearch fallback); measured 60_628
minUnionBytes: 64_200, // token-reduction Phases 1-2 (v1.69.x branch); measured union 71,379
// v3 requires a trusted helper and private state, absent from generic prompt fixtures.
// The full-audit E2E asserts actual section loading alongside report/proof behavior.
behavioral: 'external',
externalTest: 'test/skill-e2e-cso.test.ts',
maxSkeletonBytes: 18_000,
minUnionBytes: 30_000, // v3 deliberately removes the shared export/startup preamble.
mustContain: ['OWASP', 'STRIDE', 'daily', 'comprehensive', 'verif'],
// cso keeps its mode-dispatch + FP-filtering phases always-loaded, so the
// cross-cutting preamble growth (v1.57.2.0 AUQ-failure prose fallback ~2KB + the
// decision-memory nudge) lands it just over 1.05; headroom for the shared additions.
// v1.64+v1.65 merge sums both waves' preamble growth; measured 1.073.
// Existing baseline comparison remains an upper bound; absolute limits above
// preserve the compact controller and its complete domain reference.
maxSizeRatio: 1.08,
},
// ── Token-reduction Phase 4 wave 1 (v1.69.x branch) ──────────────────────
+108
View File
@@ -0,0 +1,108 @@
/** Private evaluator inputs. Never copy this module or its JSON output into producer snapshots. */
import { createHash } from 'node:crypto';
import { CORPUS_VERSION, type EvalFamily, type EvalStack } from '../fixtures/cso-eval/materialize';
export interface OracleRequest { method: 'GET' | 'POST'; path: string; body?: string; headers?: Record<string, string> }
export interface OracleResponse { status: number; body: string; headers?: Record<string, string> }
export interface PrivateOracle {
version: string;
controls: Array<{ request: OracleRequest; response: (value: OracleResponse) => boolean }>;
attack: OracleRequest;
vulnerable: (value: OracleResponse) => boolean;
fixed: (value: OracleResponse) => boolean;
/** More than the exposed training assertion: alternative attack payloads. */
heldOut: Array<{ request: OracleRequest; fixed: (value: OracleResponse) => boolean }>;
}
const get = (params: Record<string, string>): OracleRequest => ({ method: 'GET', path: `/action?${new URLSearchParams(params)}` });
const post = (value: object): OracleRequest => ({ method: 'POST', path: '/action', body: JSON.stringify(value), headers: { 'content-type': 'application/json' } });
const contains = (status: number, expected: string) => (response: OracleResponse) => response.status === status && response.body.includes(expected);
const rejects = (status: number) => (response: OracleResponse) => response.status === status;
const jsonValue = (value: OracleResponse): any => { try { return JSON.parse(value.body); } catch { return null; } };
export function oracleFor(family: EvalFamily): PrivateOracle {
const input: Record<EvalFamily, Omit<PrivateOracle, 'version'>> = {
'sql-injection': {
controls: [{ request: get({ name: 'alice' }), response: contains(200, 'HELLO_ALICE') }],
attack: get({ name: "nobody' OR 1=1 --" }), vulnerable: contains(200, 'SECRET_ADMIN_NOTE'),
fixed: value => value.status === 200 && Array.isArray(jsonValue(value)) && jsonValue(value).length === 0,
heldOut: [{ request: get({ name: "' UNION SELECT 'ORACLE_SQL_MARKER' --" }), fixed: value => value.status === 200 && !value.body.includes('ORACLE_SQL_MARKER') }],
},
'command-injection': {
controls: [{ request: get({ name: 'Ada' }), response: value => value.status === 200 && jsonValue(value)?.output === 'Ada' }],
attack: get({ name: '"; printf CSO_COMMAND_EXECUTED; #' }), vulnerable: value => value.status === 200 && jsonValue(value)?.output === 'CSO_COMMAND_EXECUTED', fixed: rejects(400),
heldOut: [{ request: get({ name: '$(printf ORACLE_COMMAND_MARKER)' }), fixed: rejects(400) }],
},
'path-traversal': {
controls: [{ request: get({ file: 'hello.txt' }), response: contains(200, 'PUBLIC_HELLO') }],
attack: get({ file: '../private.txt' }), vulnerable: contains(200, 'PRIVATE_DOCUMENT_SECRET'), fixed: rejects(403),
heldOut: [{ request: get({ file: 'subdir/../../private.txt' }), fixed: rejects(403) }],
},
ssrf: {
controls: [{ request: get({ url: 'http://127.0.0.1:8001/public' }), response: contains(200, 'PUBLIC_STATUS') }],
attack: get({ url: 'http://127.0.0.1:8001/private' }), vulnerable: contains(200, 'INTERNAL_SERVICE_SECRET'), fixed: rejects(403),
heldOut: [{ request: get({ url: 'http://localhost:8001/private' }), fixed: rejects(403) }],
},
'object-authorization': {
controls: [{ request: get({ id: 'invoice-1' }), response: value => value.status === 200 && jsonValue(value)?.total === 12 }],
attack: get({ id: 'invoice-2' }), vulnerable: value => value.status === 200 && jsonValue(value)?.total === 9800, fixed: rejects(403),
heldOut: [{ request: get({ id: 'invoice-2', owner: 'member-1' }), fixed: rejects(403) }],
},
'tenant-isolation': {
controls: [{ request: get({ tenant: 'tenant-a' }), response: contains(200, 'OWN_TENANT_DOCUMENT') }],
attack: get({ tenant: 'tenant-b' }), vulnerable: contains(200, 'OTHER_TENANT_SECRET'),
fixed: value => value.status === 200 && value.body.includes('OWN_TENANT_DOCUMENT') && !value.body.includes('OTHER_TENANT_SECRET'),
heldOut: [{ request: get({ tenant: 'tenant-b', actor_tenant: 'tenant-b' }), fixed: value => value.status === 200 && !value.body.includes('OTHER_TENANT_SECRET') }],
},
'html-injection': {
controls: [{ request: get({ name: 'Alice' }), response: contains(200, '<main>Welcome Alice</main>') }],
attack: get({ name: '<img src=x onerror="window.CSO_XSS=1">' }), vulnerable: contains(200, '<img src=x onerror='),
fixed: value => value.status === 200 && !value.body.includes('<img') && value.body.includes('&lt;img'),
heldOut: [{ request: get({ name: '</main><svg onload="window.ORACLE_XSS=1">' }), fixed: value => value.status === 200 && !value.body.includes('<svg') && value.body.includes('&lt;svg') }],
},
'open-redirect': {
controls: [{ request: get({ next: '/account' }), response: value => value.status === 302 && jsonValue(value)?.next === '/account' && value.headers?.location === '/account' }],
attack: get({ next: 'https://attacker.invalid/collect' }), vulnerable: value => value.status === 302 && value.headers?.location === 'https://attacker.invalid/collect', fixed: rejects(400),
heldOut: [{ request: get({ next: '//attacker.invalid/collect' }), fixed: rejects(400) }, { request: get({ next: '/\\attacker.invalid/collect' }), fixed: rejects(400) }],
},
'mass-assignment': {
controls: [{ request: post({ display_name: 'Ada' }), response: value => value.status === 200 && jsonValue(value)?.display_name === 'Ada' }],
attack: post({ role: 'admin' }), vulnerable: value => value.status === 200 && jsonValue(value)?.role === 'admin', fixed: value => value.status === 200 && jsonValue(value)?.role === 'member',
heldOut: [{ request: post({ id: 'member-2', role: 'owner', display_name: 'Grace' }), fixed: value => value.status === 200 && jsonValue(value)?.id === 'member-1' && jsonValue(value)?.role === 'member' && jsonValue(value)?.display_name === 'Grace' }],
},
'resource-exhaustion': {
controls: [{ request: get({ count: '5' }), response: value => value.status === 200 && jsonValue(value)?.count === 5 }],
// Bounded proof of missing admission, not an attempt to exhaust the runner.
attack: get({ count: '250' }), vulnerable: value => value.status === 200 && jsonValue(value)?.count === 250, fixed: rejects(400),
heldOut: [{ request: get({ count: '101' }), fixed: rejects(400) }, { request: get({ count: '0' }), fixed: rejects(400) }],
},
};
return { version: CORPUS_VERSION, ...input[family] };
}
export interface Observation { request: OracleRequest; response: OracleResponse }
export interface PrivateEvidence {
original: { booted: boolean; controls: Observation[]; attack: Observation };
patched: { booted: boolean; controls: Observation[]; attack: Observation; heldOut: Observation[]; existingTestsPassed: boolean };
immutableVerifier: boolean;
independentRootCauseReview: boolean;
featurePreserved: boolean;
boundaryMocks: boolean;
}
const sameRequest = (left: OracleRequest, right: OracleRequest) => JSON.stringify(left) === JSON.stringify(right);
/** Only trusted runner observations may enter this function; producer claims are not observations. */
export function judgeRepair(family: EvalFamily, evidence: PrivateEvidence): { reproduced: boolean; correctRepair: boolean; evidenceHash: string } {
const oracle = oracleFor(family);
const controls = (observations: Observation[]) => oracle.controls.every(control => observations.some(observation => sameRequest(control.request, observation.request) && control.response(observation.response)));
const original = evidence.original, patched = evidence.patched;
const reproduced = original.booted && controls(original.controls) && sameRequest(original.attack.request, oracle.attack) && oracle.vulnerable(original.attack.response);
const correctRepair = reproduced && patched.booted && controls(patched.controls) && sameRequest(patched.attack.request, oracle.attack) && oracle.fixed(patched.attack.response)
&& oracle.heldOut.every(assertion => patched.heldOut.some(observation => sameRequest(assertion.request, observation.request) && assertion.fixed(observation.response)))
&& patched.existingTestsPassed && evidence.immutableVerifier && evidence.independentRootCauseReview && evidence.featurePreserved && !evidence.boundaryMocks;
return { reproduced, correctRepair, evidenceHash: createHash('sha256').update(JSON.stringify({ version: oracle.version, family, evidence })).digest('hex') };
}
export function runtimeStart(stack: EvalStack): { executable: string; args: string[]; port: 8000; environment: Record<string, string> } {
return stack === 'node' ? { executable: '/usr/local/bin/node', args: ['app.mjs'], port: 8000, environment: {} }
: stack === 'bun' ? { executable: '/usr/local/bin/bun', args: ['--no-install', 'app.ts'], port: 8000, environment: {} }
: stack === 'python' ? { executable: '/usr/local/bin/python', args: ['-I', 'app.py'], port: 8000, environment: {} }
: { executable: '/usr/local/bin/ruby', args: ['bin/rails', 'server', '-e', 'test', '-b', '127.0.0.1', '-p', '8000'], port: 8000, environment: { RAILS_ENV: 'test', RACK_ENV: 'test' } };
}
+60
View File
@@ -0,0 +1,60 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import { dispatchCsoCommand, type CsoCliDependencies } from '../../lib/cso/cli';
import { canonical, sha256 } from '../../lib/cso/contracts';
import { validateRuntimeCatalog, type QualifiedRuntime, type RuntimeCatalog, type RuntimePlatform } from '../../lib/cso/runtime-catalog';
import { completeRuntimeCatalogFixture } from './cso-runtime-catalog';
export interface QualifiedCsoCli {
readonly catalog: RuntimeCatalog;
readonly runtime: QualifiedRuntime;
command<T = unknown>(args: string[]): Promise<T>;
}
/** Bind a staged Node image to the real command dispatcher without adding a production override. */
export function qualifiedNodeCli(options: {
image: string;
versions: Record<string, string>;
watchdogPath: string;
platform: RuntimePlatform;
}): QualifiedCsoCli {
if (!path.isAbsolute(options.watchdogPath) || !fs.statSync(options.watchdogPath).isFile()) {
throw new Error('Node lifecycle qualification requires an absolute compiled watchdog path');
}
for (const key of ['node', 'npm', 'cso-preparation']) {
if (!/^\d+\.\d+\.\d+$/.test(options.versions[key] ?? '')) {
throw new Error(`Node lifecycle qualification requires exact ${key} version metadata`);
}
}
if (options.versions['cso-preparation'] !== '1.0.0') {
throw new Error('Node lifecycle qualification requires cso-preparation 1.0.0');
}
const catalog = completeRuntimeCatalogFixture('node-lifecycle-fixture');
const runtimeIndex = catalog.runtimes.findIndex(item => item.stack === 'node' && item.platform === options.platform);
const profile = catalog.profiles.find(item => item.stack === 'node' && item.platform === options.platform);
if (runtimeIndex < 0 || !profile || !catalog.promotion) throw new Error('Node lifecycle catalog fixture is incomplete');
const runtime: QualifiedRuntime = {
...catalog.runtimes[runtimeIndex],
image: options.image,
versions: { ...options.versions },
};
catalog.runtimes[runtimeIndex] = runtime;
profile.versions = { ...runtime.versions };
catalog.promotion.evidenceDigest = `sha256:${sha256(canonical(catalog.runtimes))}`;
validateRuntimeCatalog(catalog);
const dependencies: CsoCliDependencies = Object.freeze({
runtimeCatalog: catalog,
watchdogPath: () => options.watchdogPath,
});
return Object.freeze({
catalog,
runtime,
async command<T = unknown>(args: string[]): Promise<T> {
const [command, ...commandArgs] = args;
if (!command) throw new Error('CSO qualification command is required');
return await dispatchCsoCommand(command, commandArgs, dependencies) as T;
},
});
}
+102
View File
@@ -0,0 +1,102 @@
import type { CsoStack } from '../../lib/cso/preparation';
import { canonical, sha256 } from '../../lib/cso/contracts';
import {
CSO_HELPER_ABI,
type QualifiedRuntime,
type RuntimeCatalog,
type RuntimePlatform,
} from '../../lib/cso/runtime-catalog';
const STACKS = ['node', 'bun', 'python', 'rails', 'postgresql'] as const;
const PLATFORMS = ['linux/amd64', 'linux/arm64'] as const;
const SOURCE_COMMIT = 'b'.repeat(40);
const WORKFLOW = 'https://github.com/garrytan/gstack/actions/runs/1';
const DIGEST = `sha256:${'a'.repeat(64)}`;
const VERSIONS: Record<CsoStack | 'postgresql', Record<string, string>> = {
node: { node: '24.1.0', npm: '11.3.0', 'cso-preparation': '1.0.0' },
bun: { bun: '1.3.10', 'cso-preparation': '1.0.0' },
python: { python: '3.12.9', uv: '0.8.0', 'cso-preparation': '1.0.0' },
rails: { ruby: '3.3.6', bundler: '2.6.9', 'cso-preparation': '1.0.0' },
postgresql: { postgresql: '17.2' },
};
function runtimeId(stack: CsoStack | 'postgresql', platform: RuntimePlatform): string {
return `${stack}-qualified-test-${platform === 'linux/amd64' ? 'amd64' : 'arm64'}`;
}
export function qualifiedRuntimeFixture(
stack: CsoStack | 'postgresql',
platform: RuntimePlatform = 'linux/amd64',
): QualifiedRuntime {
const arch = platform === 'linux/amd64' ? 'amd64' : 'arm64';
const qualification = stack === 'postgresql'
? {
kind: 'postgresql' as const,
sourceCommit: SOURCE_COMMIT,
workflow: WORKFLOW,
sbomDigest: DIGEST,
provenanceDigest: DIGEST,
verifiedProvenance: true as const,
containmentPassed: true as const,
coldStartPassed: true as const,
multiDatabasePassed: true as const,
readinessPassed: true as const,
}
: {
kind: 'application' as const,
sourceCommit: SOURCE_COMMIT,
workflow: WORKFLOW,
sbomDigest: DIGEST,
provenanceDigest: DIGEST,
verifiedProvenance: true as const,
containmentPassed: true as const,
coldStartPassed: true as const,
positiveNegativeAssertionsPassed: true as const,
heldOutRepairPassed: true as const,
};
return {
id: runtimeId(stack, platform),
stack,
platform,
state: 'qualified',
image: `ghcr.io/garrytan/gstack/cso-staging/${stack}-${arch}@${DIGEST}`,
entrypoint: '/opt/cso/entrypoint',
helperAbi: CSO_HELPER_ABI,
versions: { ...VERSIONS[stack] },
policyVersion: 'cso-isolation-v1',
qualifiedAt: '2026-09-09T00:00:00Z',
qualification,
};
}
/** A fresh catalog satisfying the complete reviewed and promoted runtime matrices. */
export function completeRuntimeCatalogFixture(
revision = 'runtime-test-v1',
previousRevision: string | null = null,
): RuntimeCatalog {
const runtimes = STACKS.flatMap(stack => PLATFORMS.map(platform => qualifiedRuntimeFixture(stack, platform)));
return {
schemaVersion: 1,
revision,
previousRevision,
helperAbi: CSO_HELPER_ABI,
buildRevision: 'runtime-test-build-v1',
profiles: runtimes.map(runtime => ({
id: runtime.id,
stack: runtime.stack,
platform: runtime.platform,
state: 'build_reviewed' as const,
versions: { ...runtime.versions },
reviewedAt: '2026-09-08T00:00:00Z',
})),
promotion: {
sourceCommit: SOURCE_COMMIT,
workflow: WORKFLOW,
evidenceDigest: `sha256:${sha256(canonical(runtimes))}`,
qualificationEvidenceDigest: DIGEST,
},
runtimes,
};
}
+3 -1
View File
@@ -241,7 +241,9 @@ const CARVED_INVARIANTS: ParityInvariant[] = Object.values(CARVE_GUARDS).map((g)
maxSkeletonBytes: g.maxSkeletonBytes,
minBytes: g.minUnionBytes,
mustContain: g.mustContain,
mustHaveHeadings: ['## Preamble', '## When to invoke'],
// CSO's helper trust boundary requires its private startup; demanding the
// shared Preamble here would silently reintroduce conflicting policy.
mustHaveHeadings: g.skill === 'cso' ? ['## When to invoke'] : ['## Preamble', '## When to invoke'],
maxSizeRatio: g.maxSizeRatio ?? 1.05,
}));
+129 -47
View File
@@ -1,4 +1,15 @@
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck } from './types';
import {
csoProducerChildEnvironment,
csoProducerHelperHome,
csoProducerHelperLauncher,
csoProducerProviderCommand,
csoProducerSourceDirectory,
csoProducerStateDirectory,
type ProviderAdapter,
type RunOpts,
type RunResult,
type AvailabilityCheck,
} from './types';
import { estimateCostUsd } from '../pricing';
import { execFileSync, spawnSync } from 'child_process';
import * as fs from 'fs';
@@ -19,10 +30,11 @@ export class ClaudeAdapter implements ProviderAdapter {
readonly name = 'claude';
readonly family = 'claude' as const;
async available(): Promise<AvailabilityCheck> {
async available(opts?: RunOpts): Promise<AvailabilityCheck> {
// Binary on PATH (or GSTACK_CLAUDE_BIN override). Routes through the shared
// resolver so Windows + override paths behave the same as production sites.
const resolved = resolveClaudeCommand();
const producerCommand = csoProducerProviderCommand(opts ?? { prompt: '', workdir: '/', timeoutMs: 1 });
const resolved = producerCommand ? { command: producerCommand.executable, argsPrefix: producerCommand.argsPrefix } : resolveClaudeCommand();
if (!resolved) {
return { ok: false, reason: 'claude CLI not found on PATH. Install from https://claude.ai/download or npm i -g @anthropic-ai/claude-code (or set GSTACK_CLAUDE_BIN)' };
}
@@ -35,8 +47,8 @@ export class ClaudeAdapter implements ProviderAdapter {
// secret), and any failure of `security` itself falls through to the
// not-found reason rather than throwing.
const credsPath = path.join(os.homedir(), '.claude', '.credentials.json');
const hasCreds = fs.existsSync(credsPath);
const hasKey = !!process.env.ANTHROPIC_API_KEY;
const hasCreds = !opts?.csoProducer && fs.existsSync(credsPath);
const hasKey = !!(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN);
let hasKeychain = false;
if (!hasCreds && !hasKey && process.platform === 'darwin') {
try {
@@ -50,41 +62,40 @@ export class ClaudeAdapter implements ProviderAdapter {
}
}
if (!hasCreds && !hasKey && !hasKeychain) {
return { ok: false, reason: 'No Claude auth found. Log in via `claude` interactive session, or export ANTHROPIC_API_KEY.' };
return { ok: false, reason: opts?.csoProducer
? 'No Claude producer auth found. Export ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN; macOS Keychain auth is also supported.'
: 'No Claude auth found. Log in via `claude` interactive session, or export ANTHROPIC_API_KEY.' };
}
return { ok: true };
}
async run(opts: RunOpts): Promise<RunResult> {
const start = Date.now();
const resolved = resolveClaudeCommand();
const producerCommand = csoProducerProviderCommand(opts);
const resolved = producerCommand ? { command: producerCommand.executable, argsPrefix: producerCommand.argsPrefix } : resolveClaudeCommand();
if (!resolved) {
throw new Error('claude CLI not resolvable (set GSTACK_CLAUDE_BIN or install)');
}
const model = opts.model ?? process.env.EVALS_MODEL ?? resolveEvalModel('capture');
const args = [...resolved.argsPrefix, '-p', '--output-format', 'json'];
args.push('--model', model);
if (opts.extraArgs) args.push(...opts.extraArgs);
const stateDirectory = csoProducerStateDirectory(opts);
if (stateDirectory) {
assertClaudeProducerPrerequisites();
}
try {
if (stateDirectory) prepareClaudeProducerState(stateDirectory);
const args = claudeExecArgs(opts, model, resolved.argsPrefix);
const out = execFileSync(resolved.command, args, {
input: opts.prompt,
cwd: opts.workdir,
cwd: claudeExecWorkingDirectory(opts),
timeout: opts.timeoutMs,
encoding: 'utf-8',
maxBuffer: 32 * 1024 * 1024,
// Default GSTACK_HEADLESS=1 so a benchmark run classifies as headless (an
// AskUserQuestion failure BLOCKs rather than emitting unanswerable prose).
env: { ...process.env, GSTACK_HEADLESS: '1' },
env: claudeExecEnvironment(opts, process.env),
});
const parsed = this.parseOutput(out);
return {
output: parsed.output,
tokens: parsed.tokens,
durationMs: Date.now() - start,
toolCalls: parsed.toolCalls,
modelUsed: parsed.modelUsed || model,
};
return resultFromClaudeOutput(out,{model,durationMs:Date.now()-start,producer:!!opts.csoProducer});
} catch (err: unknown) {
const durationMs = Date.now() - start;
const e = err as { code?: string; stderr?: Buffer; signal?: string; message?: string };
@@ -99,6 +110,8 @@ export class ClaudeAdapter implements ProviderAdapter {
return this.emptyResult(durationMs, { code: 'rate_limit', reason: stderr.slice(0, 400) }, model);
}
return this.emptyResult(durationMs, { code: 'unknown', reason: (e.message ?? stderr ?? 'unknown').slice(0, 400) }, model);
} finally {
if (stateDirectory) removeClaudeProducerState(stateDirectory);
}
}
@@ -106,33 +119,6 @@ export class ClaudeAdapter implements ProviderAdapter {
return estimateCostUsd(tokens, model ?? resolveEvalModel('capture'));
}
/**
* Parse claude -p --output-format json output. Shape (as of 2026-04):
* { type: "result", result: "<assistant text>", usage: { input_tokens, output_tokens, ... },
* num_turns, session_id, ... }
* Older formats may differ adapter is best-effort.
*/
private parseOutput(raw: string): { output: string; tokens: { input: number; output: number; cached?: number }; toolCalls: number; modelUsed?: string } {
try {
const obj = JSON.parse(raw);
const result = typeof obj.result === 'string' ? obj.result : String(obj.result ?? '');
const u = obj.usage ?? {};
return {
output: result,
tokens: {
input: u.input_tokens ?? 0,
output: u.output_tokens ?? 0,
cached: u.cache_read_input_tokens,
},
toolCalls: obj.num_turns ?? 0,
modelUsed: obj.model,
};
} catch {
// Non-JSON output: treat as plain text.
return { output: raw, tokens: { input: 0, output: 0 }, toolCalls: 0 };
}
}
private emptyResult(durationMs: number, error: RunResult['error'], model?: string): RunResult {
return {
output: '',
@@ -144,3 +130,99 @@ export class ClaudeAdapter implements ProviderAdapter {
};
}
}
/** Map Claude JSON output; paid producers require the documented nonblank `result` envelope. */
export function resultFromClaudeOutput(raw:string,opts:{model?:string;durationMs?:number;producer?:boolean}={}):RunResult{
const durationMs=opts.durationMs??0,defaultModel=opts.model??resolveEvalModel('capture');
try{
const obj=JSON.parse(raw),explicitError=obj?.is_error===true||(typeof obj?.subtype==='string'&&obj.subtype!=='success'),validResult=!explicitError&&typeof obj?.result==='string'&&obj.result.trim().length>0;
if(opts.producer&&!validResult)return{output:'',tokens:{input:0,output:0},durationMs,toolCalls:0,modelUsed:typeof obj?.model==='string'&&obj.model?obj.model:defaultModel,error:{code:'unknown',reason:'empty or invalid output from claude CLI (exit 0)'}};
const output=typeof obj?.result==='string'?obj.result:String(obj?.result??''),usage=obj?.usage??{};
return{output,tokens:{input:usage.input_tokens??0,output:usage.output_tokens??0,cached:usage.cache_read_input_tokens},durationMs,toolCalls:obj?.num_turns??0,modelUsed:typeof obj?.model==='string'&&obj.model?obj.model:defaultModel};
}catch{
if(opts.producer)return{output:'',tokens:{input:0,output:0},durationMs,toolCalls:0,modelUsed:defaultModel,error:{code:'unknown',reason:'empty or invalid output from claude CLI (exit 0)'}};
return{output:raw,tokens:{input:0,output:0},durationMs,toolCalls:0,modelUsed:defaultModel};
}
}
export interface ClaudeProducerPaths { root: string; home: string; workdir: string }
export function assertClaudeProducerPrerequisites(): void {
if (process.platform !== 'linux') return;
const executable = '/usr/bin/bwrap';
let stat: fs.Stats;
try { stat = fs.lstatSync(executable); } catch { throw new Error('CLAUDE_PRODUCER_REQUIRES_TRUSTED_BWRAP'); }
if (!stat.isFile() || stat.isSymbolicLink() || fs.realpathSync(executable) !== executable || stat.uid !== 0 || (stat.mode & 0o022) !== 0 || (stat.mode & 0o111) === 0) {
throw new Error('CLAUDE_PRODUCER_REQUIRES_TRUSTED_BWRAP');
}
}
export function claudeProducerPaths(stateDirectory: string): ClaudeProducerPaths {
const root = path.join(stateDirectory, 'claude-provider');
return { root, home: path.join(root, 'home'), workdir: path.join(root, 'work') };
}
export function prepareClaudeProducerState(stateDirectory: string): ClaudeProducerPaths {
const paths = claudeProducerPaths(stateDirectory);
if (fs.existsSync(paths.root)) throw new Error('CLAUDE_PRODUCER_STATE_EXISTS');
fs.mkdirSync(paths.root, { mode: 0o700 });
fs.mkdirSync(paths.home, { mode: 0o700 });
fs.mkdirSync(paths.workdir, { mode: 0o700 });
return paths;
}
export function removeClaudeProducerState(stateDirectory: string): void {
fs.rmSync(claudeProducerPaths(stateDirectory).root, { recursive: true, force: true });
}
export function claudeExecWorkingDirectory(opts: RunOpts): string {
const stateDirectory = csoProducerStateDirectory(opts);
return stateDirectory ? claudeProducerPaths(stateDirectory).workdir : opts.workdir;
}
export function claudeProducerTools(opts: RunOpts): string {
const launcher = csoProducerHelperLauncher(opts);
if (!launcher) throw new Error('CSO producer helper launcher is required');
return [`Bash(${launcher})`, `Bash(${launcher} *)`, 'Write'].join(',');
}
export function claudeExecArgs(opts: RunOpts, model: string, argsPrefix: readonly string[] = []): string[] {
const args = [...argsPrefix, '-p', '--output-format', 'json', '--model', model];
const stateDirectory = csoProducerStateDirectory(opts);
if (stateDirectory) {
if (opts.extraArgs?.length) throw new Error('CSO producer does not accept extra provider arguments');
const tools = claudeProducerTools(opts);
const sourceDirectory = csoProducerSourceDirectory(opts)!;
const helperHome = csoProducerHelperHome(opts)!;
args.push(
'--restricted',
'--safe-mode',
'--no-session-persistence',
'--permission-prompts', 'none',
'--permission-mode', 'dontAsk',
'--tools', 'Bash,Write',
'--allowed-tools', tools,
'--add-dir', claudeProducerPaths(stateDirectory).workdir,
'--add-dir', sourceDirectory,
'--add-dir', helperHome,
'--strict-mcp-config',
'--no-chrome',
);
}
if (opts.extraArgs) args.push(...opts.extraArgs);
return args;
}
export function claudeExecEnvironment(
opts: RunOpts,
source: NodeJS.ProcessEnv = process.env,
): Record<string, string> {
const stateDirectory = csoProducerStateDirectory(opts);
const paths = stateDirectory ? claudeProducerPaths(stateDirectory) : undefined;
return {
...(opts.csoProducer ? csoProducerChildEnvironment('claude', source) : source),
...(paths ? { HOME: paths.home, GSTACK_HOME: csoProducerHelperHome(opts)! } : {}),
...(stateDirectory ? { CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: '1' } : {}),
GSTACK_HEADLESS: '1',
} as Record<string, string>;
}
+144 -17
View File
@@ -1,9 +1,22 @@
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck } from './types';
import {
csoProducerChildEnvironment,
csoProducerHelperHome,
csoProducerHelperLauncher,
csoProducerProviderCommand,
csoProducerStateDirectory,
validateCsoProducerStateDirectory,
type ProviderAdapter,
type RunOpts,
type RunResult,
type AvailabilityCheck,
} from './types';
import { estimateCostUsd } from '../pricing';
import { execFileSync, spawnSync } from 'child_process';
import { randomBytes } from 'node:crypto';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { atomicWriteSync } from '../../../lib/fs-atomic';
export type GeminiStreamParse = {
output: string;
@@ -112,15 +125,16 @@ export class GeminiAdapter implements ProviderAdapter {
readonly name = 'gemini';
readonly family = 'gemini' as const;
async available(): Promise<AvailabilityCheck> {
const res = spawnSync('sh', ['-c', 'command -v gemini'], { timeout: 2000 });
async available(opts?: RunOpts): Promise<AvailabilityCheck> {
const producerCommand = csoProducerProviderCommand(opts ?? { prompt: '', workdir: '/', timeoutMs: 1 });
const res = producerCommand ? { status: 0 } : spawnSync('sh', ['-c', 'command -v gemini'], { timeout: 2000 });
if (res.status !== 0) {
return { ok: false, reason: 'gemini CLI not found on PATH. Install per https://github.com/google-gemini/gemini-cli' };
}
const legacyCfgDir = path.join(os.homedir(), '.config', 'gemini');
const newCfgDir = path.join(os.homedir(), '.gemini');
const newOauth = path.join(newCfgDir, 'oauth_creds.json');
const hasCfg = fs.existsSync(legacyCfgDir) || fs.existsSync(newOauth);
const hasCfg = !opts?.csoProducer && (fs.existsSync(legacyCfgDir) || fs.existsSync(newOauth));
// CLI accepts either name; Google AI Studio keys are usually GEMINI_API_KEY.
const hasKey = !!(process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY);
if (!hasCfg && !hasKey) {
@@ -138,23 +152,15 @@ export class GeminiAdapter implements ProviderAdapter {
// Default to --yolo (non-interactive) and stream-json output so we can parse
// tokens + tool calls. Callers can override via extraArgs. (--skip-trust was
// removed in gemini-cli 0.34; passing it errors at argv parse.)
const args = ['-p', opts.prompt, '--output-format', 'stream-json', '--yolo'];
if (opts.model) args.push('--model', opts.model);
if (opts.extraArgs) args.push(...opts.extraArgs);
try {
const out = execFileSync('gemini', args, {
cwd: opts.workdir,
const args = geminiExecArgs(opts);
const command = csoProducerProviderCommand(opts);
const out = execFileSync(command?.executable ?? 'gemini', [...(command?.argsPrefix ?? []), ...args], {
cwd: geminiExecWorkingDirectory(opts),
timeout: opts.timeoutMs,
encoding: 'utf-8',
maxBuffer: 32 * 1024 * 1024,
env: {
...process.env,
// Prefer GEMINI_API_KEY when only that is set (CLI reads both).
...(process.env.GEMINI_API_KEY && !process.env.GOOGLE_API_KEY
? { GOOGLE_API_KEY: process.env.GEMINI_API_KEY }
: {}),
},
env: geminiExecEnvironment(opts, process.env),
});
return resultFromGeminiStream(out, { model: opts.model, durationMs: Date.now() - start });
} catch (err: unknown) {
@@ -189,3 +195,124 @@ export class GeminiAdapter implements ProviderAdapter {
};
}
}
export interface GeminiProducerPaths {
root: string;
home: string;
workdir: string;
systemDefaults: string;
systemSettings: string;
}
export function geminiProducerPaths(stateDirectory: string): GeminiProducerPaths {
validateCsoProducerStateDirectory(stateDirectory);
const root = path.join(stateDirectory, 'gemini-provider');
return {
root,
home: path.join(root, 'home'),
workdir: path.join(root, 'work'),
systemDefaults: path.join(root, 'system-defaults.json'),
systemSettings: path.join(root, 'system-settings.json'),
};
}
/** Highest-precedence Gemini policy for a clean, one-cell producer host. */
const GEMINI_PRODUCER_BLOCKED_ENVIRONMENT = [
'GEMINI_API_KEY', 'GOOGLE_API_KEY', 'GOOGLE_CLOUD_PROJECT', 'GOOGLE_CLOUD_LOCATION',
] as const;
export function geminiProducerSystemSettings(contextFileName: string, helperLauncher: string) {
if (!/^\.gstack-cso-context-[a-f0-9]{32}\.md$/.test(contextFileName)) throw new Error('INVALID_GEMINI_CONTEXT_FILENAME');
const helper = csoProducerHelperLauncher({ prompt: '', workdir: '/', timeoutMs: 1, csoProducer: { stateDirectory: '/', sourceDirectory: '/source', helperLauncher, helperGeneration: path.join(path.dirname(helperLauncher), '.gstack-cso-generation'), providerCommand: { executable: '/provider', argsPrefix: [] } } });
if (!helper) throw new Error('CSO producer helper launcher is required');
const tools = [`run_shell_command(${helper})`, 'write_file'];
return {
advanced: { ignoreLocalEnv: true },
admin: {
extensions: { enabled: false },
mcp: { enabled: false },
skills: { enabled: false },
},
context: {
fileName: [contextFileName],
includeDirectoryTree: false,
loadMemoryFromIncludeDirectories: false,
memoryBoundaryMarkers: [],
},
hooksConfig: { enabled: false },
privacy: { usageStatisticsEnabled: false },
security: {
environmentVariableRedaction: { allowed: [], blocked: [...GEMINI_PRODUCER_BLOCKED_ENVIRONMENT], enabled: true },
folderTrust: { enabled: false },
toolSandboxing: false,
},
skills: { enabled: false },
telemetry: { enabled: false, logPrompts: false },
tools: { allowed: tools, core: tools, sandbox: false },
};
}
export function prepareGeminiProducerState(stateDirectory: string, helperLauncher: string): GeminiProducerPaths {
const paths = geminiProducerPaths(stateDirectory);
if (fs.existsSync(paths.root)) throw new Error('GEMINI_PRODUCER_STATE_EXISTS');
fs.mkdirSync(paths.root, { mode: 0o700 });
fs.mkdirSync(paths.home, { mode: 0o700 });
fs.mkdirSync(paths.workdir, { mode: 0o700 });
const contextFileName = `.gstack-cso-context-${randomBytes(16).toString('hex')}.md`;
atomicWriteSync(paths.systemDefaults, '{}\n', { mode: 0o600, noReplace: true });
atomicWriteSync(paths.systemSettings, `${JSON.stringify(geminiProducerSystemSettings(contextFileName, helperLauncher), null, 2)}\n`, { mode: 0o600, noReplace: true });
return paths;
}
export function removeGeminiProducerState(stateDirectory: string): void {
const paths = geminiProducerPaths(stateDirectory);
fs.rmSync(paths.root, { recursive: true, force: true });
}
export function geminiExecArgs(opts: RunOpts): string[] {
const stateDirectory = csoProducerStateDirectory(opts);
const args = ['-p', opts.prompt, '--output-format', 'stream-json'];
if (stateDirectory) {
if (opts.extraArgs?.length) throw new Error('CSO producer does not accept extra provider arguments');
args.push(
'--approval-mode', 'yolo',
'--include-directories', geminiProducerPaths(stateDirectory).workdir,
'-e', 'none',
);
} else {
args.push('--yolo');
}
if (opts.model) args.push('--model', opts.model);
if (opts.extraArgs) args.push(...opts.extraArgs);
return args;
}
export function geminiExecWorkingDirectory(opts: RunOpts): string {
const stateDirectory = csoProducerStateDirectory(opts);
return stateDirectory ? geminiProducerPaths(stateDirectory).workdir : opts.workdir;
}
export function geminiExecEnvironment(
opts: RunOpts,
source: NodeJS.ProcessEnv = process.env,
): Record<string, string> {
const stateDirectory = csoProducerStateDirectory(opts);
const paths = stateDirectory ? geminiProducerPaths(stateDirectory) : undefined;
const env = paths ? csoProducerChildEnvironment('gemini', source) : { ...source } as Record<string, string>;
// Prefer GEMINI_API_KEY when only that is set (CLI reads both).
if (env.GEMINI_API_KEY && !env.GOOGLE_API_KEY) env.GOOGLE_API_KEY = env.GEMINI_API_KEY;
if (paths) {
env.HOME = paths.home;
env.GSTACK_HOME = csoProducerHelperHome(opts)!;
env.GEMINI_SANDBOX = 'false';
env.GEMINI_TELEMETRY_ENABLED = 'false';
env.GEMINI_TELEMETRY_LOG_PROMPTS = 'false';
env.GEMINI_CLI_TRUST_WORKSPACE = 'true';
env.GEMINI_SYSTEM_MD = 'false';
env.GEMINI_WRITE_SYSTEM_MD = 'false';
env.GEMINI_CLI_HOME = paths.home;
env.GEMINI_CLI_SYSTEM_DEFAULTS_PATH = paths.systemDefaults;
env.GEMINI_CLI_SYSTEM_SETTINGS_PATH = paths.systemSettings;
}
return env;
}
+154 -60
View File
@@ -1,10 +1,23 @@
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck } from './types';
import {
csoProducerChildEnvironment,
csoProducerHelperGeneration,
csoProducerHelperHome,
csoProducerProviderCommand,
csoProducerSourceDirectory,
csoProducerStateDirectory,
CSO_PRODUCER_SHELL_ENV,
type ProviderAdapter,
type RunOpts,
type RunResult,
type AvailabilityCheck,
} from './types';
import { estimateCostUsd } from '../pricing';
import { execFileSync, spawnSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { CODEX_FRONTIER_MODEL } from '../../../scripts/resolvers/constants';
import { atomicWriteSync } from '../../../lib/fs-atomic';
/**
* GPT adapter wraps the OpenAI `codex` CLI (codex exec with --json output).
@@ -17,45 +30,40 @@ export class GptAdapter implements ProviderAdapter {
readonly name = 'gpt';
readonly family = 'gpt' as const;
async available(): Promise<AvailabilityCheck> {
const res = spawnSync('sh', ['-c', 'command -v codex'], { timeout: 2000 });
async available(opts?: RunOpts): Promise<AvailabilityCheck> {
const producerCommand = csoProducerProviderCommand(opts ?? { prompt: '', workdir: '/', timeoutMs: 1 });
const res = producerCommand ? { status: 0 } : spawnSync('sh', ['-c', 'command -v codex'], { timeout: 2000 });
if (res.status !== 0) {
return { ok: false, reason: 'codex CLI not found on PATH. Install: npm i -g @openai/codex' };
}
// Auth sniff: ~/.codex/ should contain auth state after `codex login`
const codexDir = path.join(os.homedir(), '.codex');
if (!fs.existsSync(codexDir)) {
return { ok: false, reason: 'No ~/.codex/ found. Run `codex login` to authenticate via ChatGPT.' };
const hasFileAuth = !opts?.csoProducer && fs.existsSync(codexDir);
if (!hasFileAuth && !process.env.OPENAI_API_KEY) {
return { ok: false, reason: 'No Codex auth found. Paid CSO producers require OPENAI_API_KEY; other evals may use `codex login`.' };
}
return { ok: true };
}
async run(opts: RunOpts): Promise<RunResult> {
const start = Date.now();
// `-s read-only` is load-bearing safety. With `--skip-git-repo-check` we
// bypass codex's interactive trust prompt for unknown directories (benchmarks
// often run in temp dirs / non-git paths), so the read-only sandbox is now
// the only boundary preventing codex from mutating the workdir. If you ever
// remove `-s read-only`, drop `--skip-git-repo-check` too.
// Existing callers retain `-s read-only`. CSO producer calls use an isolated
// CODEX_HOME with a reviewed permission profile written below.
const model = opts.model ?? process.env.GSTACK_CODEX_MODEL ?? CODEX_FRONTIER_MODEL;
const args = ['exec', opts.prompt, '-C', opts.workdir, '-s', 'read-only', '--skip-git-repo-check', '--json', '-m', model];
if (opts.extraArgs) args.push(...opts.extraArgs);
const stateDirectory = csoProducerStateDirectory(opts);
try {
const out = execFileSync('codex', args, {
cwd: opts.workdir,
if (stateDirectory) prepareCodexProducerState(opts);
const args = codexExecArgs(opts, model);
const command = csoProducerProviderCommand(opts);
const out = execFileSync(command?.executable ?? 'codex', [...(command?.argsPrefix ?? []), ...args], {
cwd: codexExecWorkingDirectory(opts),
timeout: opts.timeoutMs,
encoding: 'utf-8',
maxBuffer: 32 * 1024 * 1024,
...(opts.csoProducer ? { env: codexExecEnvironment(opts, process.env) } : {}),
});
const parsed = this.parseJsonl(out);
return {
output: parsed.output,
tokens: parsed.tokens,
durationMs: Date.now() - start,
toolCalls: parsed.toolCalls,
modelUsed: parsed.modelUsed || model,
};
return resultFromCodexStream(out,{model,durationMs:Date.now()-start,producer:!!opts.csoProducer});
} catch (err: unknown) {
const durationMs = Date.now() - start;
const e = err as { code?: string; stderr?: Buffer; signal?: string; message?: string };
@@ -70,6 +78,8 @@ export class GptAdapter implements ProviderAdapter {
return this.emptyResult(durationMs, { code: 'rate_limit', reason: stderr.slice(0, 400) }, model);
}
return this.emptyResult(durationMs, { code: 'unknown', reason: (e.message ?? stderr ?? 'unknown').slice(0, 400) }, model);
} finally {
if (stateDirectory) removeCodexProducerState(stateDirectory);
}
}
@@ -77,44 +87,6 @@ export class GptAdapter implements ProviderAdapter {
return estimateCostUsd(tokens, model ?? CODEX_FRONTIER_MODEL);
}
/**
* Parse codex exec --json JSONL stream.
* Key events:
* - item.completed with item.type === 'agent_message' text output
* - item.completed with item.type === 'command_execution' tool call
* - turn.completed usage.input_tokens, usage.output_tokens
* - thread.started session id (not used here)
*/
private parseJsonl(raw: string): { output: string; tokens: { input: number; output: number }; toolCalls: number; modelUsed?: string } {
let output = '';
let input = 0;
let out = 0;
let toolCalls = 0;
let modelUsed: string | undefined;
for (const line of raw.split('\n')) {
const s = line.trim();
if (!s) continue;
try {
const obj = JSON.parse(s);
if (obj.type === 'item.completed' && obj.item) {
if (obj.item.type === 'agent_message' && typeof obj.item.text === 'string') {
output += (output ? '\n' : '') + obj.item.text;
} else if (obj.item.type === 'command_execution') {
toolCalls += 1;
}
} else if (obj.type === 'turn.completed') {
const u = obj.usage ?? {};
input += u.input_tokens ?? 0;
out += u.output_tokens ?? 0;
if (obj.model) modelUsed = obj.model;
}
} catch {
// skip malformed lines — codex stderr can leak in
}
}
return { output, tokens: { input, output: out }, toolCalls, modelUsed };
}
private emptyResult(durationMs: number, error: RunResult['error'], model?: string): RunResult {
return {
output: '',
@@ -126,3 +98,125 @@ export class GptAdapter implements ProviderAdapter {
};
}
}
/** Map `codex exec --json` output while requiring evidence-bearing text for paid producers. */
export function resultFromCodexStream(raw:string,opts:{model?:string;durationMs?:number;producer?:boolean}={}):RunResult{
let output='',input=0,out=0,toolCalls=0,modelUsed:string|undefined;
for(const line of raw.split('\n')){
const text=line.trim();if(!text)continue;
try{
const obj=JSON.parse(text);
if(obj.type==='item.completed'&&obj.item){
if(obj.item.type==='agent_message'&&typeof obj.item.text==='string')output+=(output?'\n':'')+obj.item.text;
else if(obj.item.type==='command_execution')toolCalls++;
}else if(obj.type==='turn.completed'){
const usage=obj.usage??{};input+=usage.input_tokens??0;out+=usage.output_tokens??0;
if(typeof obj.model==='string'&&obj.model)modelUsed=obj.model;
}
}catch{/* Codex can mix diagnostic text into the JSONL stream. */}
}
const durationMs=opts.durationMs??0,resolvedModel=modelUsed||opts.model||CODEX_FRONTIER_MODEL;
if(opts.producer&&!output.trim())return{output:'',tokens:{input:0,output:0},durationMs,toolCalls:0,modelUsed:resolvedModel,error:{code:'unknown',reason:'empty output from codex CLI (exit 0)'}};
return{output,tokens:{input,output:out},durationMs,toolCalls,modelUsed:resolvedModel};
}
export interface CodexProducerPaths {
root: string;
home: string;
workdir: string;
config: string;
}
export function codexProducerPaths(stateDirectory: string): CodexProducerPaths {
const root = path.join(stateDirectory, 'codex-provider');
return { root, home: path.join(root, 'home'), workdir: path.join(root, 'work'), config: path.join(root, 'home', 'config.toml') };
}
export function codexProducerConfig(opts: RunOpts): string {
const launcher = opts.csoProducer?.helperLauncher;
if (!launcher) throw new Error('CSO producer helper launcher is required');
const sourceDirectory = csoProducerSourceDirectory(opts);
if (!sourceDirectory) throw new Error('CSO producer source directory is required');
const helperHome = csoProducerHelperHome(opts);
if (!helperHome) throw new Error('CSO producer helper home is required');
const generation = csoProducerHelperGeneration(opts);
if (!generation) throw new Error('CSO producer helper generation is required');
const directory = path.dirname(launcher), suffix = process.platform === 'win32' ? '.exe' : '';
const trustedArtifacts = [
path.join(directory, `cso-eval-producer${suffix}`), launcher,
path.join(directory, `gstack-cso-core${suffix}`), path.join(directory, `gstack-cso-watchdog${suffix}`),
generation,
];
const grants = [...trustedArtifacts, sourceDirectory].map(file => `${JSON.stringify(file)} = "read"`).join('\n');
return `approval_policy = "never"
default_permissions = "cso-producer"
allow_login_shell = false
check_for_update_on_startup = false
[shell_environment_policy]
inherit = "all"
include_only = ${JSON.stringify(CSO_PRODUCER_SHELL_ENV)}
ignore_default_excludes = false
experimental_use_profile = false
[permissions.cso-producer]
description = "CSO producer: private state plus one immutable source snapshot"
[permissions.cso-producer.filesystem]
":root" = "deny"
":minimal" = "read"
${grants}
${JSON.stringify(helperHome)} = "write"
[permissions.cso-producer.filesystem.":workspace_roots"]
"." = "write"
[permissions.cso-producer.network]
enabled = false
`;
}
export function prepareCodexProducerState(opts: RunOpts): CodexProducerPaths {
const stateDirectory = csoProducerStateDirectory(opts);
if (!stateDirectory) throw new Error('CSO producer state directory is required');
const paths = codexProducerPaths(stateDirectory);
if (fs.existsSync(paths.root)) throw new Error('CODEX_PRODUCER_STATE_EXISTS');
fs.mkdirSync(paths.root, { mode: 0o700 });
fs.mkdirSync(paths.home, { mode: 0o700 });
fs.mkdirSync(paths.workdir, { mode: 0o700 });
atomicWriteSync(paths.config, codexProducerConfig(opts), { mode: 0o600, noReplace: true });
return paths;
}
export function removeCodexProducerState(stateDirectory: string): void {
fs.rmSync(codexProducerPaths(stateDirectory).root, { recursive: true, force: true });
}
/** Exported so free tests can inspect the exact paid-CLI boundary without running it. */
export function codexExecArgs(opts: RunOpts, model: string): string[] {
const stateDirectory = csoProducerStateDirectory(opts);
const args = ['exec', opts.prompt, '-C', stateDirectory ? codexProducerPaths(stateDirectory).workdir : opts.workdir];
if (stateDirectory) {
if (opts.extraArgs?.length) throw new Error('CSO producer does not accept extra provider arguments');
args.push(
'--strict-config', '--ephemeral', '--ignore-rules', '--skip-git-repo-check',
);
} else {
args.push('-s', 'read-only', '--skip-git-repo-check');
}
args.push('--json', '-m', model);
if (opts.extraArgs) args.push(...opts.extraArgs);
return args;
}
export function codexExecWorkingDirectory(opts: RunOpts): string {
const stateDirectory = csoProducerStateDirectory(opts);
return stateDirectory ? codexProducerPaths(stateDirectory).workdir : opts.workdir;
}
export function codexExecEnvironment(opts: RunOpts, source: NodeJS.ProcessEnv = process.env): Record<string, string> {
if (!opts.csoProducer) return source as Record<string, string>;
const stateDirectory = csoProducerStateDirectory(opts)!;
const paths = codexProducerPaths(stateDirectory);
return { ...csoProducerChildEnvironment('gpt', source), HOME: paths.home, CODEX_HOME: paths.home, GSTACK_HOME: csoProducerHelperHome(opts)! };
}
+99 -1
View File
@@ -1,3 +1,5 @@
import * as path from 'node:path';
/**
* Provider adapter interface uniform contract for Claude, GPT, Gemini.
*
@@ -18,6 +20,19 @@ export interface RunOpts {
model?: string;
/** Extra flags per-provider (escape hatch for rare cases). Prefer staying generic. */
extraArgs?: string[];
/** Producer-only execution policy. Omit to preserve each adapter's defaults. */
csoProducer?: {
/** The one explicit state directory exposed outside the source worktree. */
stateDirectory: string;
/** Exact validated per-cell source root. Providers may grant only this path read-only. */
sourceDirectory: string;
/** Absolute immutable launcher path admitted by the producer. */
helperLauncher: string;
/** Exact adjacent generation manifest read by the launcher. */
helperGeneration: string;
/** Exact provider command whose bytes/version are bound into the receipt. */
providerCommand: { executable: string; argsPrefix: string[] };
};
}
export interface TokenUsage {
@@ -57,6 +72,89 @@ export interface AvailabilityCheck {
export type Family = 'claude' | 'gpt' | 'gemini';
export const CSO_PRODUCER_SHELL_ENV = [
'PATH', 'HOME', 'LANG', 'LC_ALL', 'TZ',
'GSTACK_HOME', 'GSTACK_SESSION_KIND', 'GSTACK_HEADLESS',
] as const;
const CSO_AUTH_ENV: Record<Family, readonly string[]> = {
claude: ['ANTHROPIC_API_KEY', 'CLAUDE_CODE_OAUTH_TOKEN'],
gpt: ['OPENAI_API_KEY'],
gemini: ['GEMINI_API_KEY', 'GOOGLE_API_KEY', 'GOOGLE_CLOUD_PROJECT', 'GOOGLE_CLOUD_LOCATION'],
};
export function validateCsoProducerStateDirectory(stateDirectory: string): string {
if (!path.isAbsolute(stateDirectory) || path.normalize(stateDirectory) !== stateDirectory) {
throw new Error('CSO producer state directory must be an absolute normalized path');
}
return stateDirectory;
}
export function csoProducerStateDirectory(opts: RunOpts): string | undefined {
if (!opts.csoProducer) return undefined;
csoProducerHelperLauncher(opts);
csoProducerHelperGeneration(opts);
csoProducerSourceDirectory(opts);
csoProducerProviderCommand(opts);
return validateCsoProducerStateDirectory(opts.csoProducer.stateDirectory);
}
export function csoProducerSourceDirectory(opts: RunOpts): string | undefined {
if (!opts.csoProducer) return undefined;
return validateCsoProducerStateDirectory(opts.csoProducer.sourceDirectory);
}
export function csoProducerHelperHome(opts: RunOpts): string | undefined {
const stateDirectory = csoProducerStateDirectory(opts);
return stateDirectory ? path.join(stateDirectory, 'cso-home') : undefined;
}
export function csoProducerProviderCommand(opts: RunOpts): { executable: string; argsPrefix: string[] } | undefined {
if (!opts.csoProducer) return undefined;
const command = opts.csoProducer.providerCommand;
if (!command || !path.isAbsolute(command.executable) || path.normalize(command.executable) !== command.executable ||
!Array.isArray(command.argsPrefix) || command.argsPrefix.some(value => typeof value !== 'string' || value.includes('\0'))) {
throw new Error('CSO producer provider command must be an absolute normalized trusted path');
}
return { executable: command.executable, argsPrefix: [...command.argsPrefix] };
}
export function csoProducerHelperLauncher(opts: RunOpts): string | undefined {
if (!opts.csoProducer) return undefined;
const launcher = opts.csoProducer.helperLauncher;
if (typeof launcher !== 'string') throw new Error('CSO producer helper launcher must be an absolute normalized trusted path');
const suffix = process.platform === 'win32' ? '.exe' : '';
if (!path.isAbsolute(launcher) || path.normalize(launcher) !== launcher || path.basename(launcher) !== `gstack-cso-launcher${suffix}` ||
!/^[A-Za-z0-9_./:\\-]+$/.test(launcher)) {
throw new Error('CSO producer helper launcher must be an absolute normalized trusted path');
}
return launcher;
}
export function csoProducerHelperGeneration(opts: RunOpts): string | undefined {
if (!opts.csoProducer) return undefined;
const generation = opts.csoProducer.helperGeneration;
const launcher = csoProducerHelperLauncher(opts);
if (typeof generation !== 'string' || !path.isAbsolute(generation) || path.normalize(generation) !== generation ||
path.basename(generation) !== '.gstack-cso-generation' || path.dirname(generation) !== path.dirname(launcher!) ||
!/^[A-Za-z0-9_./:\\-]+$/.test(generation)) {
throw new Error('CSO producer helper generation must be the exact adjacent absolute normalized trusted path');
}
return generation;
}
/** Copy only execution inputs needed by the selected producer host. */
export function csoProducerChildEnvironment(
family: Family,
source: NodeJS.ProcessEnv = process.env,
): Record<string, string> {
const output: Record<string, string> = {};
for (const key of [...CSO_PRODUCER_SHELL_ENV, ...CSO_AUTH_ENV[family]]) {
const value = source[key];
if (typeof value === 'string' && !value.includes('\0')) output[key] = value;
}
return output;
}
export interface ProviderAdapter {
/** Stable name used in output tables and config (e.g., 'claude', 'gpt', 'gemini'). */
readonly name: string;
@@ -66,7 +164,7 @@ export interface ProviderAdapter {
* Check whether the provider's CLI binary is present and authenticated.
* Should never block >2s. Non-throwing: returns { ok: false, reason } on failure.
*/
available(): Promise<AvailabilityCheck>;
available(opts?: RunOpts): Promise<AvailabilityCheck>;
/** Run a prompt and return normalized RunResult. Non-throwing. Errors go in result.error. */
run(opts: RunOpts): Promise<RunResult>;
/** Estimate USD cost for the reported token usage and model. */
+233 -4
View File
@@ -1,16 +1,245 @@
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { dirname, join } from 'node:path';
import { execFileSync } from 'node:child_process';
import { ClaudeAdapter } from './helpers/providers/claude';
import { GptAdapter } from './helpers/providers/gpt';
import { ClaudeAdapter, claudeExecArgs, claudeExecEnvironment, claudeExecWorkingDirectory, claudeProducerPaths, resultFromClaudeOutput } from './helpers/providers/claude';
import { codexExecArgs, codexExecEnvironment, codexExecWorkingDirectory, codexProducerConfig, codexProducerPaths, GptAdapter, resultFromCodexStream } from './helpers/providers/gpt';
import { geminiExecArgs, geminiExecEnvironment, geminiExecWorkingDirectory, geminiProducerPaths, geminiProducerSystemSettings } from './helpers/providers/gemini';
import { CSO_PRODUCER_SHELL_ENV, csoProducerChildEnvironment } from './helpers/providers/types';
const ENV_KEYS = ['PATH', 'GSTACK_CLAUDE_BIN', 'GSTACK_CLAUDE_BIN_ARGS',
'GSTACK_CODEX_MODEL', 'EVALS_MODEL', 'GSTACK_EVAL_MODEL', 'GSTACK_EVAL_MODEL_CAPTURE'];
let saved: Record<string, string | undefined>;
let workdir: string;
describe('CSO producer provider policies', () => {
const suffix = process.platform === 'win32' ? '.exe' : '';
const helperAt = (directory: string) => join(directory, `gstack-cso-launcher${suffix}`);
const providerAt = (directory: string) => ({ executable: join(directory, `provider${suffix}`), argsPrefix: [] as string[] });
const policy = (stateDirectory: string, sourceDirectory: string, helperLauncher: string) => ({
stateDirectory, sourceDirectory, helperLauncher, helperGeneration: join(dirname(helperLauncher), '.gstack-cso-generation'), providerCommand: providerAt(dirname(helperLauncher)),
});
test('keeps exact Codex read-only defaults and gives the producer a custom least-privilege profile', () => {
const providerWorkdir = join(tmpdir(), 'gstack-provider-work');
const common = { prompt: 'Reply OK', workdir: providerWorkdir, timeoutMs: 5000 };
expect(codexExecArgs(common, 'gpt-test')).toEqual([
'exec', 'Reply OK', '-C', providerWorkdir,
'-s', 'read-only', '--skip-git-repo-check', '--json', '-m', 'gpt-test',
]);
const state = join(providerWorkdir, 'state');
const source = join(providerWorkdir, 'source');
const helper = helperAt(join(providerWorkdir, 'installed'));
const producer = { ...common, csoProducer: policy(state, source, helper) };
expect(codexExecArgs(producer, 'gpt-test')).toEqual([
'exec', 'Reply OK', '-C', codexProducerPaths(state).workdir,
'--strict-config', '--ephemeral', '--ignore-rules', '--skip-git-repo-check',
'--json', '-m', 'gpt-test',
]);
expect(codexExecWorkingDirectory(common)).toBe(providerWorkdir);
expect(codexExecWorkingDirectory(producer)).toBe(codexProducerPaths(state).workdir);
const config = codexProducerConfig(producer);
expect(config).toContain('default_permissions = "cso-producer"');
expect(config).toContain('":root" = "deny"\n":minimal" = "read"');
expect(config).toContain('[permissions.cso-producer.filesystem.":workspace_roots"]\n"." = "write"');
expect(config).toContain('[permissions.cso-producer.network]\nenabled = false');
expect(config).toContain(`include_only = ${JSON.stringify(CSO_PRODUCER_SHELL_ENV)}`);
expect(config).toContain(`${JSON.stringify(join(state, 'cso-home'))} = "write"`);
for (const admitted of [
join(dirname(helper), `cso-eval-producer${suffix}`), helper,
join(dirname(helper), `gstack-cso-core${suffix}`), join(dirname(helper), `gstack-cso-watchdog${suffix}`),
join(dirname(helper), '.gstack-cso-generation'), source,
]) expect(config).toContain(`${JSON.stringify(admitted)} = "read"`);
expect(config).not.toContain(`${JSON.stringify(dirname(helper))} = "read"`);
expect(config).not.toContain(`${JSON.stringify(dirname(source))} = "read"`);
expect(config).not.toContain('OPENAI_API_KEY');
expect(() => codexExecArgs({ ...common, csoProducer: policy('relative-state', source, helper) }, 'gpt-test')).toThrow('absolute normalized path');
expect(() => codexExecArgs({ ...common, csoProducer: policy(state, 'relative-source', helper) }, 'gpt-test')).toThrow('absolute normalized path');
expect(() => codexExecArgs({ ...common, csoProducer: { ...policy(state, source, helper), helperGeneration: join(dirname(helper), 'wrong-generation') } }, 'gpt-test')).toThrow('exact adjacent');
expect(codexExecArgs(producer, 'gpt-test').join(' ')).not.toMatch(/(?:^|\s)(?:-s|--sandbox)(?:\s|$)|danger|bypass|approve-for-me/);
});
test('keeps exact Claude defaults and enables its producer-only noninteractive safe policy', () => {
const work = join(tmpdir(), 'gstack-claude-work');
const state = join(work, 'state');
const source = join(work, 'source');
const helper = helperAt(join(work, 'installed'));
const common = { prompt: 'Reply OK', workdir: work, timeoutMs: 5000 };
expect(claudeExecArgs(common, 'claude-test', ['wrapper'])).toEqual([
'wrapper', '-p', '--output-format', 'json', '--model', 'claude-test',
]);
const producer = { ...common, csoProducer: policy(state, source, helper) };
expect(claudeExecArgs(producer, 'claude-test')).toEqual([
'-p', '--output-format', 'json', '--model', 'claude-test',
'--restricted', '--safe-mode', '--no-session-persistence',
'--permission-prompts', 'none', '--permission-mode', 'dontAsk',
'--tools', 'Bash,Write',
'--allowed-tools', `Bash(${helper}),Bash(${helper} *),Write`,
'--add-dir', claudeProducerPaths(state).workdir,
'--add-dir', source,
'--add-dir', join(state, 'cso-home'),
'--strict-mcp-config', '--no-chrome',
]);
expect(claudeExecWorkingDirectory(common)).toBe(work);
expect(claudeExecWorkingDirectory(producer)).toBe(claudeProducerPaths(state).workdir);
expect(claudeExecArgs(producer, 'claude-test').join(' ')).not.toMatch(/bypassPermissions|danger/);
});
test('keeps exact Gemini defaults and replaces deprecated yolo only for the producer', () => {
const work = join(tmpdir(), 'gstack-gemini-work');
const state = join(work, 'state');
const source = join(work, 'source');
const helper = helperAt(join(work, 'installed'));
const common = { prompt: 'Reply OK', workdir: work, timeoutMs: 5000, model: 'gemini-test' };
expect(geminiExecArgs(common)).toEqual([
'-p', 'Reply OK', '--output-format', 'stream-json', '--yolo', '--model', 'gemini-test',
]);
const producer = { ...common, csoProducer: policy(state, source, helper) };
const producerArgs = geminiExecArgs(producer);
expect(producerArgs).toEqual([
'-p', 'Reply OK', '--output-format', 'stream-json',
'--approval-mode', 'yolo', '--include-directories', geminiProducerPaths(state).workdir, '-e', 'none',
'--model', 'gemini-test',
]);
expect(producerArgs).not.toContain(work);
expect(producerArgs).not.toContain(source);
expect(geminiExecWorkingDirectory(common)).toBe(work);
expect(geminiExecWorkingDirectory(producer)).toBe(geminiProducerPaths(state).workdir);
const contextFileName = `.gstack-cso-context-${'a'.repeat(32)}.md`;
expect(geminiProducerSystemSettings(contextFileName, helper)).toEqual({
advanced: { ignoreLocalEnv: true },
admin: { extensions: { enabled: false }, mcp: { enabled: false }, skills: { enabled: false } },
context: { fileName: [contextFileName], includeDirectoryTree: false, loadMemoryFromIncludeDirectories: false, memoryBoundaryMarkers: [] },
hooksConfig: { enabled: false },
privacy: { usageStatisticsEnabled: false },
security: {
environmentVariableRedaction: { allowed: [], blocked: ['GEMINI_API_KEY', 'GOOGLE_API_KEY', 'GOOGLE_CLOUD_PROJECT', 'GOOGLE_CLOUD_LOCATION'], enabled: true },
folderTrust: { enabled: false }, toolSandboxing: false,
},
skills: { enabled: false },
telemetry: { enabled: false, logPrompts: false },
tools: { allowed: [`run_shell_command(${helper})`, 'write_file'], core: [`run_shell_command(${helper})`, 'write_file'], sandbox: false },
});
expect(() => geminiProducerSystemSettings('GEMINI.md', helper)).toThrow('INVALID_GEMINI_CONTEXT_FILENAME');
});
test('passes only selected provider auth plus safe execution inputs and strips Docker credentials', () => {
const fixtureRoot = join(tmpdir(), 'gstack-provider-environment-policy');
const state = join(fixtureRoot, 'state');
const source = join(fixtureRoot, 'source');
const helpers = join(fixtureRoot, 'helpers');
const work = join(fixtureRoot, 'work');
const sourceEnv = {
PATH: '/usr/bin', HOME: '/home/eval', LANG: 'C.UTF-8', LC_ALL: 'C.UTF-8', TZ: 'UTC',
GSTACK_HOME: state, GSTACK_SESSION_KIND: 'spawned', GSTACK_HEADLESS: '1',
OPENAI_API_KEY: 'openai-auth', ANTHROPIC_API_KEY: 'anthropic-auth', CLAUDE_CODE_OAUTH_TOKEN: 'claude-auth',
GEMINI_API_KEY: 'gemini-auth', GOOGLE_CLOUD_PROJECT: 'project',
CSO_EVAL_PAID: '1', AWS_SECRET_ACCESS_KEY: 'unrelated',
DOCKER_HOST: 'tcp://remote.example:2376', DOCKER_CONFIG: '/credentials', DOCKER_CERT_PATH: '/certs',
};
const safe = { PATH: '/usr/bin', HOME: '/home/eval', LANG: 'C.UTF-8', LC_ALL: 'C.UTF-8', TZ: 'UTC', GSTACK_HOME: state, GSTACK_SESSION_KIND: 'spawned', GSTACK_HEADLESS: '1' };
expect(csoProducerChildEnvironment('gpt', sourceEnv)).toEqual({ ...safe, OPENAI_API_KEY: 'openai-auth' });
expect(csoProducerChildEnvironment('claude', sourceEnv)).toEqual({ ...safe, ANTHROPIC_API_KEY: 'anthropic-auth', CLAUDE_CODE_OAUTH_TOKEN: 'claude-auth' });
expect(csoProducerChildEnvironment('gemini', sourceEnv)).toEqual({ ...safe, GEMINI_API_KEY: 'gemini-auth', GOOGLE_CLOUD_PROJECT: 'project' });
const helper = helperAt(helpers);
const producer = policy(state, source, helper);
const common = { prompt: '', workdir: work, timeoutMs: 1, csoProducer: producer };
const codexPaths = codexProducerPaths(state);
expect(codexExecEnvironment(common, sourceEnv)).toEqual({
...safe, HOME: codexPaths.home, GSTACK_HOME: join(state, 'cso-home'), CODEX_HOME: codexPaths.home, OPENAI_API_KEY: 'openai-auth',
});
const claudePaths = claudeProducerPaths(state);
expect(claudeExecEnvironment(common, sourceEnv)).toEqual({
...safe, HOME: claudePaths.home, GSTACK_HOME: join(state, 'cso-home'),
ANTHROPIC_API_KEY: 'anthropic-auth', CLAUDE_CODE_OAUTH_TOKEN: 'claude-auth', CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: '1',
});
const geminiPaths = geminiProducerPaths(state);
expect(geminiExecEnvironment(common, sourceEnv)).toEqual({
...safe, HOME: geminiPaths.home, GSTACK_HOME: join(state, 'cso-home'),
GEMINI_API_KEY: 'gemini-auth', GOOGLE_API_KEY: 'gemini-auth', GOOGLE_CLOUD_PROJECT: 'project',
GEMINI_SANDBOX: 'false', GEMINI_TELEMETRY_ENABLED: 'false', GEMINI_TELEMETRY_LOG_PROMPTS: 'false',
GEMINI_CLI_TRUST_WORKSPACE: 'true', GEMINI_SYSTEM_MD: 'false', GEMINI_WRITE_SYSTEM_MD: 'false',
GEMINI_CLI_HOME: geminiPaths.home,
GEMINI_CLI_SYSTEM_DEFAULTS_PATH: geminiPaths.systemDefaults,
GEMINI_CLI_SYSTEM_SETTINGS_PATH: geminiPaths.systemSettings,
});
for (const env of [codexExecEnvironment(common, sourceEnv), claudeExecEnvironment(common, sourceEnv), geminiExecEnvironment(common, sourceEnv)]) {
expect(env.CSO_EVAL_PAID).toBeUndefined();
expect(env.AWS_SECRET_ACCESS_KEY).toBeUndefined();
expect(env.DOCKER_HOST).toBeUndefined();
}
});
test('preserves default provider environments and rejects producer escape-hatch arguments', () => {
const fixtureRoot = join(tmpdir(), 'gstack-provider-default-policy');
const state = join(fixtureRoot, 'state');
const source = join(fixtureRoot, 'source');
const helpers = join(fixtureRoot, 'helpers');
const work = join(fixtureRoot, 'work');
const sourceEnv = { PATH: '/bin', CSO_EVAL_PAID: '1', UNRELATED_SECRET: 'kept-by-default', GEMINI_API_KEY: 'key' };
const common = { prompt: '', workdir: work, timeoutMs: 1 };
const producer = policy(state, source, helperAt(helpers));
expect(codexExecEnvironment(common, sourceEnv)).toBe(sourceEnv);
expect(claudeExecEnvironment(common, sourceEnv)).toEqual({ ...sourceEnv, GSTACK_HEADLESS: '1' });
expect(geminiExecEnvironment(common, sourceEnv)).toEqual({ ...sourceEnv, GOOGLE_API_KEY: 'key' });
for (const invoke of [
() => codexExecArgs({ ...common, csoProducer: producer, extraArgs: ['--unsafe'] }, 'model'),
() => claudeExecArgs({ ...common, csoProducer: producer, extraArgs: ['--unsafe'] }, 'model'),
() => geminiExecArgs({ ...common, csoProducer: producer, extraArgs: ['--unsafe'] }),
]) expect(invoke).toThrow('does not accept extra provider arguments');
});
test('fails paid Codex producers closed on empty or malformed success output',()=>{
for(const raw of ['', ' \n', 'not json\n{bad', '{"type":"turn.completed","usage":{"input_tokens":9,"output_tokens":2}}']){
expect(resultFromCodexStream(raw,{model:'gpt-test',producer:true})).toMatchObject({output:'',error:{code:'unknown',reason:'empty output from codex CLI (exit 0)'}});
}
expect(resultFromCodexStream('diagnostic\n{"type":"item.completed","item":{"type":"agent_message","text":"OK"}}',{producer:true}).error).toBeUndefined();
expect(resultFromCodexStream('',{producer:false}).error).toBeUndefined();
});
test('requires Claude producer JSON with a nonblank string result',()=>{
for(const raw of ['', 'plain text', '{}', '{"result":42}', '{"result":" "}', '{"type":"result","subtype":"success","is_error":true,"result":"API Error: connection failed"}', '{"type":"result","subtype":"error_during_execution","result":"partial"}']){
expect(resultFromClaudeOutput(raw,{model:'claude-test',producer:true})).toMatchObject({output:'',error:{code:'unknown',reason:'empty or invalid output from claude CLI (exit 0)'}});
}
const valid=resultFromClaudeOutput('{"result":"OK"}',{producer:true});expect(valid.output).toBe('OK');expect(valid.error).toBeUndefined();
const legacy=resultFromClaudeOutput('plain text');expect(legacy.output).toBe('plain text');expect(legacy.error).toBeUndefined();
});
test.skipIf(process.platform === 'win32')('executes the exact receipt-bound provider command despite a hostile PATH', async () => {
const fixture = mkdtempSync(join(tmpdir(), 'cso-bound-provider-'));
const previousPath = process.env.PATH;
try {
const state = join(fixture, 'state');
const source = join(fixture, 'source');
const installed = join(fixture, 'installed');
const hostile = join(fixture, 'hostile');
for (const directory of [state, source, installed, hostile]) mkdirSync(directory);
const bound = join(installed, 'codex');
const boundMarker = join(fixture, 'bound-ran');
const hostileMarker = join(fixture, 'hostile-ran');
writeFileSync(bound, `#!/bin/sh\ntouch ${JSON.stringify(boundMarker)}\nprintf '%s\\n' '{"type":"item.completed","item":{"type":"agent_message","text":"BOUND"}}'\n`, { mode: 0o755 });
writeFileSync(join(hostile, 'codex'), `#!/bin/sh\ntouch ${JSON.stringify(hostileMarker)}\nprintf '%s\\n' '{"type":"item.completed","item":{"type":"agent_message","text":"HOSTILE"}}'\n`, { mode: 0o755 });
process.env.PATH = `${hostile}:${previousPath ?? ''}`;
const helper = helperAt(installed);
const result = await new GptAdapter().run({
prompt: 'Reply OK', workdir: fixture, timeoutMs: 5000, model: 'gpt-test',
csoProducer: { ...policy(state, source, helper), providerCommand: { executable: bound, argsPrefix: [] } },
});
expect(result.output).toBe('BOUND');
expect(existsSync(boundMarker)).toBe(true);
expect(existsSync(hostileMarker)).toBe(false);
expect(existsSync(codexProducerPaths(state).root)).toBe(false);
} finally {
if (previousPath === undefined) delete process.env.PATH;
else process.env.PATH = previousPath;
rmSync(fixture, { recursive: true, force: true });
}
});
});
// Both adapters execute these stubs, so a regression can never launch a paid CLI.
describe.skipIf(process.platform === 'win32')('provider model selection', () => {
beforeEach(() => {
@@ -78,7 +78,8 @@ describe("#1539 confidence resolver — pre-emit verification gate present", ()
describe("#1539 generated SKILL.md files — gate propagated to all consumers", () => {
const consumers = [
"review/SKILL.md",
"cso/SKILL.md",
// CSO's private startup uses its dedicated evidence rubric rather than
// the shared numerical confidence resolver guarded by this regression.
"plan-eng-review/SKILL.md",
"ship/SKILL.md",
];
+2 -1
View File
@@ -40,7 +40,8 @@ const GENERATED_WITH_GUIDANCE = [
'autoplan/sections/design-phase.md',
'autoplan/sections/eng-phase.md',
'autoplan/sections/dx-phase.md',
'cso/SKILL.md',
// CSO's private startup does not import the shared synchronous-dispatch
// guidance and its bounded worker policy is specified in its own skeleton.
'design-consultation/SKILL.md',
'design-review/SKILL.md',
'design-shotgun/SKILL.md',
+3 -1
View File
@@ -73,11 +73,13 @@ esac
if [ "$1" = run ] && [ "$2" = build ]; then
printf 'build\\n' >> "$FIXTURE_EVENTS"
"$FIXTURE_REAL_BUN" run scripts/gen-skill-docs.ts --host all
for target in browse/dist/browse design/dist/design make-pdf/dist/pdf; do
for target in browse/dist/browse design/dist/design make-pdf/dist/pdf bin/gstack-cso-core bin/gstack-cso-launcher bin/gstack-cso-watchdog; do
mkdir -p "$(dirname "$target")"
printf '#!/usr/bin/env bash\\nexit 0\\n' > "$target"
chmod +x "$target"
done
printf '%064d\\n' 0 > bin/.gstack-cso-generation
printf 'complete\\n' > browse/dist/.build-complete
exit 0
fi
if [ "$1" = run ] && [ "$2" = gen:skill-docs ]; then
+3
View File
@@ -35,6 +35,9 @@ const BUILT_ARTIFACT_ALLOWLIST = [
'design/dist/',
'make-pdf/dist/',
'bin/gstack-global-discover', // compiled from bin/gstack-global-discover.ts at build time
'bin/gstack-cso-core',
'bin/gstack-cso-launcher',
'bin/gstack-cso-watchdog',
];
/**
+137 -7
View File
@@ -1,8 +1,9 @@
/**
* setup: the NEEDS_BUILD decision ("# 1. Build browse binary if needed").
*
* One `bun run build` produces every binary (browse, design, make-pdf), so a
* missing or stale one of ANY of them must trigger the whole build. Before,
* Direct `bun run build` produces every binary. Setup includes CSO when its
* host capability probe succeeds, and otherwise builds the general binaries
* while removing CSO artifacts so /cso fails closed. Before,
* only the browse binary's existence was checked and lib/ was not in the
* staleness set: a missing design/dist/design or make-pdf/dist/pdf, or an edit
* to lib/ (the canonical claude-bin / error-handling / aside-render sources the
@@ -22,12 +23,16 @@ import { runBashScript } from './helpers/bash-script';
const ROOT = path.resolve(import.meta.dir, '..');
const SETUP_SRC = fs.readFileSync(path.join(ROOT, 'setup'), 'utf-8');
const BUILD_SRC = fs.readFileSync(path.join(ROOT, 'scripts/build.sh'), 'utf-8');
const CSO_BUILD_SRC = fs.readFileSync(path.join(ROOT, 'scripts/build-cso.sh'), 'utf-8');
// From the $_EXE suffix derivation through the `fi` that closes the staleness
// chain. The statement that follows (the build itself) is the end anchor and is
// NOT included, so the harness never tries to run `bun run build`.
const BLOCK_START = '_EXE=""';
const BLOCK_END = '\nif [ "$NEEDS_BUILD" -eq 1 ]; then';
const CSO_SWITCH_START = 'if [ "${GSTACK_SETUP_RUNNING:-0}" = "1" ] && [ "${GSTACK_SETUP_SKIP_CSO_BUILD:-0}" = "1" ]; then';
const CSO_SWITCH_END = '\nbash browse/scripts/build-node-server.sh';
function needsBuildBlock(): string {
const start = SETUP_SRC.indexOf(BLOCK_START);
@@ -36,9 +41,17 @@ function needsBuildBlock(): string {
return SETUP_SRC.slice(start, end + 1);
}
function csoBuildSwitchBlock(): string {
const start = BUILD_SRC.indexOf(CSO_SWITCH_START);
const end = BUILD_SRC.indexOf(CSO_SWITCH_END, start);
if (start < 0 || end < 0) throw new Error('Could not locate the setup-private CSO build switch');
return BUILD_SRC.slice(start, end);
}
// Fixed instants, far apart, so coarse filesystem timestamps and clock skew
// can never blur "older than the binary" into "newer".
const BIN_T = new Date('2024-06-01T12:00:00Z');
const STAMP_T = new Date('2024-07-01T12:00:00Z');
const OLD_T = new Date('2024-01-01T12:00:00Z');
const NEW_T = new Date('2024-12-01T12:00:00Z');
@@ -50,6 +63,10 @@ const SOURCE_FILES = [
'lib/claude-bin.ts',
'package.json',
'bun.lock',
'scripts/build.sh',
'scripts/build-cso.sh',
'scripts/build-cso-windows.ps1',
'lib/cso/launcher.c',
];
const tmpDirs: string[] = [];
@@ -76,7 +93,19 @@ function makeTree(opts: { exe?: string } = {}): string {
writeAt(path.join(dir, 'browse/dist/browse'), BIN_T, 0o755);
writeAt(path.join(dir, `design/dist/design${exe}`), BIN_T, 0o755);
writeAt(path.join(dir, `make-pdf/dist/pdf${exe}`), BIN_T, 0o755);
writeAt(path.join(dir, `bin/gstack-cso-core${exe}`), BIN_T, 0o755);
writeAt(path.join(dir, `bin/gstack-cso-launcher${exe}`), BIN_T, 0o755);
const generation = path.join(dir, 'bin/.gstack-cso-generation');
fs.writeFileSync(generation, `${'a'.repeat(64)}\n`, { mode: 0o600 });
fs.utimesSync(generation, BIN_T, BIN_T);
if (exe) {
const generationLock = path.join(dir, 'bin/.gstack-cso-generation.lock');
fs.writeFileSync(generationLock, '', { mode: 0o600 });
fs.utimesSync(generationLock, BIN_T, BIN_T);
}
if (!exe) writeAt(path.join(dir, 'bin/gstack-cso-watchdog'), BIN_T, 0o755);
for (const f of SOURCE_FILES) writeAt(path.join(dir, f), OLD_T);
writeAt(path.join(dir, 'browse/dist/.build-complete'), STAMP_T);
return dir;
}
@@ -84,12 +113,13 @@ function touchNewer(dir: string, rel: string): void {
writeAt(path.join(dir, rel), NEW_T);
}
function decide(dir: string, opts: { isWindows?: '0' | '1' } = {}): number {
function decide(dir: string, opts: { isWindows?: '0' | '1'; csoAvailable?: '0' | '1' } = {}): number {
const script = [
'set -e',
`SOURCE_GSTACK_DIR="${dir}"`,
'BROWSE_BIN="$SOURCE_GSTACK_DIR/browse/dist/browse"',
`IS_WINDOWS=${opts.isWindows ?? '0'}`,
`CSO_BUILD_AVAILABLE=${opts.csoAvailable ?? '1'}`,
needsBuildBlock(),
'echo "NEEDS_BUILD=$NEEDS_BUILD"',
].join('\n');
@@ -113,12 +143,62 @@ describe('setup: NEEDS_BUILD static invariants', () => {
expect(block).not.toContain('bun_cmd run build');
});
test('all three binaries are existence-checked with -x and the $_EXE suffix', () => {
test('all required binaries are existence-checked with -x and the $_EXE suffix', () => {
const block = needsBuildBlock();
expect(block).toContain('[ ! -x "$BROWSE_BIN" ]');
expect(block).toContain('[ ! -x "$SOURCE_GSTACK_DIR/design/dist/design$_EXE" ]');
expect(block).toContain('[ ! -x "$SOURCE_GSTACK_DIR/make-pdf/dist/pdf$_EXE" ]');
expect(block).toContain('if [ "$IS_WINDOWS" -eq 1 ]; then _EXE=".exe"; fi');
expect(block).toContain('if [ "$CSO_BUILD_AVAILABLE" -eq 1 ]; then');
});
test('setup owns the only CSO build escape hatch and probes Bun hardening flags', () => {
for (const flag of ['dotenv', 'bunfig', 'tsconfig', 'package-json']) {
expect(SETUP_SRC).toContain(`--no-compile-autoload-${flag}`);
}
expect(SETUP_SRC).toContain('probe_cso_build_prerequisites');
expect(BUILD_SRC).toContain('[ "${GSTACK_SETUP_RUNNING:-0}" = "1" ]');
expect(BUILD_SRC).toContain('[ "${GSTACK_SETUP_SKIP_CSO_BUILD:-0}" = "1" ]');
expect(BUILD_SRC).toContain('BUN_CMD="$BUN_CMD" bash scripts/build-cso.sh');
});
test('the skip flag alone cannot weaken a direct build; setup can omit and purge CSO', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-cso-build-switch-'));
tmpDirs.push(dir);
for (const sub of ['bin', 'scripts']) fs.mkdirSync(path.join(dir, sub), { recursive: true });
for (const name of ['gstack-cso-launcher', 'gstack-cso-launcher.exe', 'gstack-cso-core', 'gstack-cso-core.exe', 'gstack-cso-watchdog']) {
writeAt(path.join(dir, 'bin', name), BIN_T, 0o755);
}
const strict = path.join(dir, 'scripts/build-cso.sh');
fs.writeFileSync(strict, '#!/bin/sh\nprintf called > cso-called\n');
fs.chmodSync(strict, 0o755);
let result = runBashScript(`set -e\n${csoBuildSwitchBlock()}`, {
cwd: dir, env: { ...process.env, GSTACK_SETUP_SKIP_CSO_BUILD: '1' },
});
expect(result.status).toBe(0);
expect(fs.existsSync(path.join(dir, 'cso-called'))).toBe(true);
fs.unlinkSync(path.join(dir, 'cso-called'));
result = runBashScript(`set -e\n${csoBuildSwitchBlock()}`, {
cwd: dir, env: { ...process.env, GSTACK_SETUP_RUNNING: '1', GSTACK_SETUP_SKIP_CSO_BUILD: '1' },
});
expect(result.status).toBe(0);
expect(fs.existsSync(path.join(dir, 'cso-called'))).toBe(false);
expect(fs.readdirSync(path.join(dir, 'bin')).filter(name => name.startsWith('gstack-cso-'))).toEqual([]);
});
test('the whole-build stamp is invalidated before compilation and published only after every output succeeds', () => {
const invalidate = BUILD_SRC.indexOf('rm -f "$BUILD_STAMP" "$BUILD_STAMP_TMP"');
const firstBuild = BUILD_SRC.indexOf('"$BUN_CMD" run vendor:xterm');
const csoBuild = BUILD_SRC.indexOf('BUN_CMD="$BUN_CMD" bash scripts/build-cso.sh');
const publish = BUILD_SRC.indexOf('mv -f "$BUILD_STAMP_TMP" "$BUILD_STAMP"');
expect(invalidate).toBeGreaterThan(-1);
expect(firstBuild).toBeGreaterThan(invalidate);
expect(csoBuild).toBeGreaterThan(firstBuild);
expect(publish).toBeGreaterThan(csoBuild);
expect(BUILD_SRC.slice(publish + 1)).not.toContain('"$BUN_CMD" build');
expect(CSO_BUILD_SRC).toContain('rm -f "$CSO_BUILD_ROOT/browse/dist/.build-complete"');
});
test('the staleness find walks every embedded source root, lib/ included', () => {
@@ -126,9 +206,9 @@ describe('setup: NEEDS_BUILD static invariants', () => {
for (const root of ['browse/src', 'make-pdf/src', 'design/src', 'lib']) {
expect(block).toContain(`"$SOURCE_GSTACK_DIR/${root}"`);
}
expect(block).toContain('-type f -newer "$BROWSE_BIN"');
expect(block).toContain('"$SOURCE_GSTACK_DIR/package.json" -nt "$BROWSE_BIN"');
expect(block).toContain('[ -f "$SOURCE_GSTACK_DIR/bun.lock" ] && [ "$SOURCE_GSTACK_DIR/bun.lock" -nt "$BROWSE_BIN" ]');
expect(block).toContain('-type f -newer "$BUILD_STAMP"');
expect(block).toContain('"$SOURCE_GSTACK_DIR/package.json" -nt "$BUILD_STAMP"');
expect(block).toContain('[ -f "$SOURCE_GSTACK_DIR/bun.lock" ] && [ "$SOURCE_GSTACK_DIR/bun.lock" -nt "$BUILD_STAMP" ]');
});
});
@@ -137,6 +217,16 @@ describe('setup: NEEDS_BUILD decision executes', () => {
expect(decide(makeTree())).toBe(0);
});
test('an interrupted partial build cannot let a refreshed browse binary hide stale CSO outputs', () => {
const dir = makeTree();
fs.unlinkSync(path.join(dir, 'browse/dist/.build-complete'));
writeAt(path.join(dir, 'browse/dist/browse'), NEW_T, 0o755);
expect(fs.statSync(path.join(dir, 'bin/gstack-cso-core')).mtimeMs).toBeLessThan(
fs.statSync(path.join(dir, 'browse/dist/browse')).mtimeMs,
);
expect(decide(dir)).toBe(1);
});
test('make-pdf/dist/pdf missing → 1 (was: not checked at all)', () => {
const dir = makeTree();
fs.unlinkSync(path.join(dir, 'make-pdf/dist/pdf'));
@@ -155,6 +245,46 @@ describe('setup: NEEDS_BUILD decision executes', () => {
expect(decide(dir)).toBe(1);
});
test.each(['bin/gstack-cso-launcher','bin/gstack-cso-core', 'bin/gstack-cso-watchdog'])('%s missing → 1', binary => {
const dir = makeTree();
fs.unlinkSync(path.join(dir, binary));
expect(decide(dir)).toBe(1);
});
test('CSO generation manifest missing → 1', () => {
const dir = makeTree();
fs.unlinkSync(path.join(dir, 'bin/.gstack-cso-generation'));
expect(decide(dir)).toBe(1);
});
test('Windows CSO generation lock missing → 1', () => {
const dir = makeTree({ exe: '.exe' });
fs.unlinkSync(path.join(dir, 'bin/.gstack-cso-generation.lock'));
expect(decide(dir, { isWindows: '1' })).toBe(1);
});
test('unavailable CSO removes stale helpers and does not force a repeat build', () => {
const dir = makeTree();
expect(decide(dir, { csoAvailable: '0' })).toBe(0);
for (const binary of ['bin/gstack-cso-launcher', 'bin/gstack-cso-core', 'bin/gstack-cso-watchdog', 'bin/.gstack-cso-generation']) {
expect(fs.existsSync(path.join(dir, binary))).toBe(false);
}
});
test('unavailable CSO ignores its build-script freshness but still rebuilds missing general binaries', () => {
const dir = makeTree();
touchNewer(dir, 'scripts/build-cso.sh');
expect(decide(dir, { csoAvailable: '0' })).toBe(0);
fs.unlinkSync(path.join(dir, 'design/dist/design'));
expect(decide(dir, { csoAvailable: '0' })).toBe(1);
});
test.each(['scripts/build.sh', 'scripts/build-cso.sh', 'scripts/build-cso-windows.ps1', 'lib/cso/launcher.c'])('%s changed → 1', source => {
const dir = makeTree();
touchNewer(dir, source);
expect(decide(dir)).toBe(1);
});
// MSYS bash has no execute bit: `[ -x file ]` is true for any regular file, so
// this case is POSIX-only.
test.skipIf(process.platform === 'win32')('a binary that exists but is not executable counts as missing → 1', () => {
+24
View File
@@ -0,0 +1,24 @@
import { describe, expect, test } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
const template = fs.readFileSync(
path.join(import.meta.dir, '..', '.github', 'PULL_REQUEST_TEMPLATE.md'),
'utf8',
);
describe('gstack PR liveness policy', () => {
test('remembers the authenticated repository-owner exemption', () => {
expect(template).toContain('Repository owner @garrytan is explicitly exempt');
expect(template).toContain('gh api user --jq .login');
expect(template).toMatch(/Git author metadata\s+alone is not sufficient/);
expect(template).toContain('PR author is @garrytan (owner exemption)');
});
test('retains live GSTACK PR proof for every other contributor', () => {
expect(template).toContain('required for external contributors');
expect(template).toContain('All other contributors');
expect(template).toContain('`GSTACK PR` typed LIVE');
expect(template).toContain('not drawn,\noverlaid, or edited onto the image');
});
});
+6 -20
View File
@@ -2,9 +2,9 @@
* AUQ behavioral matrix drive each AUQ-heavy skill to its first
* AskUserQuestion and grade it to plan-ceo's bar (periodic, paid, SDK capture).
*
* Layer 0 (auq-format-always-loaded.test.ts) deterministically guarantees every
* skill SHIPS the format spec in its always-loaded skeleton. This test proves
* each skill's model OBEYS it: that the first real AUQ each skill fires is a
* Layer 0 (auq-format-always-loaded.test.ts) deterministically guarantees each
* listed skill SHIPS the format spec in its always-loaded skeleton. This test
* proves each skill's model OBEYS it: that the first real AUQ it fires is a
* compliant decision brief (all 7 format elements) with a substantive
* recommendation (>= 4). One parametrized case per skill so a single weak skill
* is an isolated failure, not a blocker for the rest.
@@ -20,7 +20,9 @@
* choices) are intentionally OUT of this matrix; Layer 0 covers their format
* spec, and a fixture can't fairly trigger their AUQ.
*
* Run a subset in the foreground with AUQ_MATRIX_ONLY="plan-eng-review,cso".
* CSO is intentionally absent: its private startup omits the shared AUQ block,
* and its dedicated E2E grades evidence, reporting, and proof behavior.
* Run a subset in the foreground with AUQ_MATRIX_ONLY="plan-eng-review,spec".
*/
import { test } from 'bun:test';
import { CAPTURE_MS } from './helpers/eval-budgets';
@@ -53,17 +55,6 @@ pricing page, a Postgres entitlements table, and a Redis cache — no tests
mentioned, no rollout plan, no auth check on the upgrade endpoint.
`;
const VULN_CODE = `export function login(req, res) {
// builds SQL by string concat; sets a session cookie with no flags
const user = db.query("SELECT * FROM users WHERE name = '" + req.body.name + "'");
if (user && user.password === req.body.password) {
res.cookie('session', user.id); // no HttpOnly, Secure, SameSite, or expiry
return res.json({ ok: true });
}
return res.status(401).json({ ok: false });
}
`;
interface MatrixSkill {
skill: string;
fixtures: Record<string, string>;
@@ -93,11 +84,6 @@ const MATRIX: MatrixSkill[] = [
fixtures: {},
scenario: 'The founder says: "I am building an AI tool that auto-writes unit tests for any repo. I think it is a great idea but I have zero users. Should I build it, and how do I get my first users?" Run the office-hours diagnostic until the first AskUserQuestion.',
},
{
skill: 'cso',
fixtures: { 'server/auth.js': VULN_CODE },
scenario: 'Audit the code in this repo (server/auth.js) for security issues. Walk the audit until the first AskUserQuestion (scope/stack confirmation or first finding).',
},
{
skill: 'spec',
fixtures: {},
+220 -271
View File
@@ -1,289 +1,238 @@
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import { CAPTURE_MS, CAPTURE_LONG_MS } from './helpers/eval-budgets';
import { test, expect, afterAll } from 'bun:test';
import { CAPTURE_LONG_MS } from './helpers/eval-budgets';
import { runSkillTest } from './helpers/session-runner';
import {
ROOT, runId, evalsEnabled,
describeIfSelected, logCost, recordE2E,
createEvalCollector, finalizeEvalCollector,
} from './helpers/e2e-helpers';
import { spawnSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { ROOT, runId, describeIfSelected, logCost, recordE2E, createEvalCollector, finalizeEvalCollector } from './helpers/e2e-helpers';
import { validateCoverage, validateFinding, completeness, type RunReportV3 } from '../lib/cso/contracts';
import { spawnSync } from 'node:child_process';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
const evalCollector = createEvalCollector('e2e-cso');
// runSkillTest can drain stderr for 5s after its unchanged CLI deadline.
// Let cleanup and failure recording finish before Bun starts a retry.
const CAPTURE_CLEANUP_MS = 6_000;
let captureSequence = 0;
afterAll(() => finalizeEvalCollector(evalCollector));
afterAll(() => {
finalizeEvalCollector(evalCollector);
});
// --- CSO v2 E2E Tests ---
describeIfSelected('CSO v2 — full audit', ['cso-full-audit'], () => {
let csoDir: string;
beforeAll(() => {
csoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-cso-'));
const run = (cmd: string, args: string[]) =>
spawnSync(cmd, args, { cwd: csoDir, stdio: 'pipe', timeout: 5000 });
run('git', ['init', '-b', 'main']);
run('git', ['config', 'user.email', 'test@test.com']);
run('git', ['config', 'user.name', 'Test']);
// Create a minimal app with a planted vulnerability
fs.writeFileSync(path.join(csoDir, 'package.json'), JSON.stringify({
name: 'cso-test-app',
version: '1.0.0',
dependencies: { express: '4.18.0' },
}, null, 2));
// Planted vuln: hardcoded API key
fs.writeFileSync(path.join(csoDir, 'server.ts'), `
import express from 'express';
const app = express();
const API_KEY = "sk-1234567890abcdef1234567890abcdef";
app.get('/api/data', (req, res) => {
const id = req.query.id;
res.json({ data: \`result for \${id}\` });
});
app.listen(3000);
`);
// Planted vuln: .env tracked by git
fs.writeFileSync(path.join(csoDir, '.env'), 'DATABASE_URL=postgres://admin:secretpass@prod.db.example.com:5432/myapp\n');
run('git', ['add', '.']);
run('git', ['commit', '-m', 'initial']);
// Synthetic credential-shaped canary: its bytes must never appear in agent output or saved reports.
const CANARY = ['ghp_', 'f7K3mV9nT2qR8sL4xP6dW0bH5jY1cZ3aF9uE'].join('');
interface Fixture { repo: string; state: string; original: Map<string, string>; }
function git(repo: string, ...args: string[]): string {
const result = spawnSync('git', args, { cwd: repo, encoding: 'utf8', timeout: 5000 });
if (result.status !== 0) throw new Error(`Fixture git ${args[0]} failed: ${result.stderr}`);
return result.stdout;
}
function fixture(files: Record<string, string>, diff?: Record<string, string>): Fixture {
const repo = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-cso-v3-'));
const state = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-cso-state-'));
git(repo, 'init', '-b', 'main');
git(repo, 'config', 'user.email', 'test@example.invalid');
git(repo, 'config', 'user.name', 'CSO Test');
const write = (inputs: Record<string, string>) => {
for (const [name, content] of Object.entries(inputs)) {
fs.mkdirSync(path.dirname(path.join(repo, name)), { recursive: true });
fs.writeFileSync(path.join(repo, name), content);
}
git(repo, 'add', '.');
git(repo, 'commit', '-m', 'CSO fixture');
};
write(files);
if (diff) { git(repo, 'checkout', '-b', 'fixture-change'); write(diff); }
return { repo, state, original: new Map(Object.entries({ ...files, ...diff })) };
}
function removeFixture(f: Fixture): void {
fs.rmSync(f.repo, { recursive: true, force: true });
fs.rmSync(f.state, { recursive: true, force: true });
}
async function withFixture<T>(files:Record<string,string>,diff:Record<string,string>|undefined,run:(fixture:Fixture)=>Promise<T>):Promise<T>{const f=fixture(files,diff);try{return await run(f);}finally{removeFixture(f);}}
function reportsUnder(dir: string): string[] {
// Bounded to the exact private namespace; no report in source or old v2 namespace is accepted.
if (!fs.existsSync(dir)) return [];
return fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
const child = path.join(dir, entry.name);
return entry.isDirectory() ? reportsUnder(child) : entry.name === 'report.json' ? [child] : [];
});
}
function requireReport(f: Fixture): RunReportV3 {
const reports = reportsUnder(path.join(f.state, 'security', 'cso'));
expect(reports).toHaveLength(1); // A mandatory assertion: absent reports used to silently pass.
const raw = fs.readFileSync(reports[0], 'utf8');
expect(raw).not.toContain(CANARY);
expect(raw).not.toContain(fs.realpathSync(f.repo));
const report: RunReportV3 = JSON.parse(raw);
expect(report.schemaVersion).toBe(3);
expect(report.status).toBe('finished');
expect(report.source.root).toBe('<REDACTED-internal.user_path>');
expect(report.policy.mode).toBe('daily');
expect(report.policy.offline).toBe(true);
expect(report.coverage.length).toBeGreaterThan(0);
report.coverage.forEach(validateCoverage);
expect(report.completeness).toBe(completeness(report));
for (const finding of report.findings) {
const { id, fingerprint, reproduction, repair, closure, verificationId, reproductionAttemptId, ...submitted } = finding;
validateFinding(submitted);
expect(id).toBe(fingerprint);
expect(id).toMatch(/^[a-f0-9]{32}$/);
expect(finding.evidence).toBe('supported');
expect(reproduction).toBe('not_attempted');
expect(repair).toBe('not_attempted');
expect(closure).toBe('open');
expect(verificationId).toBeUndefined();
expect(reproductionAttemptId).toBeUndefined();
}
const markdown = fs.readFileSync(path.join(path.dirname(reports[0]), 'report.md'), 'utf8');
expect(markdown).toMatch(/^(complete|partial|not assessed) — /);
expect(markdown).not.toContain(CANARY);
if (process.platform !== 'win32') expect(fs.statSync(reports[0]).mode & 0o777).toBe(0o600);
for (const [name, content] of f.original) expect(fs.readFileSync(path.join(f.repo, name), 'utf8')).toBe(content);
expect(git(f.repo, 'status', '--porcelain=v1', '--untracked-files=all')).toBe('');
return report;
}
function prompt(f: Fixture, flags: string): string {
return `Read ${path.join(ROOT, 'cso/SKILL.md')} and perform /cso ${flags} --offline --base main on ${f.repo}.
The trusted installed helper for this evaluation is ${path.join(ROOT, 'bin', process.platform === 'win32' ? 'gstack-cso-launcher.exe' : 'gstack-cso-launcher')}.
GSTACK_HOME is already set to the private evaluation state directory.
Complete the helper-owned report even when coverage is partial. Do not use AskUserQuestion or request new scope.
Use one sequential challenge (independent agent unavailable for this evaluation); do not spawn agents.
This is a static audit: do not execute the application, test provider credentials, or install dependencies.
Do not read or execute files outside this repository except the trusted skill/helper and its private run artifacts.`;
}
afterAll(() => {
try { fs.rmSync(csoDir, { recursive: true, force: true }); } catch {}
});
const PACKAGE = JSON.stringify({ name: 'cso-v3-evaluation', version: '1.0.0', private: true });
test('/cso finds planted vulnerabilities', async () => {
const result = await runSkillTest({
testName: 'cso-full-audit',
runId: `cso-full-audit-${process.env.EVALS_RUN_ID ?? runId}-${process.pid}-${++captureSequence}`,
publicStreamDiagnostics: true,
prompt: `Read the file ${path.join(ROOT, 'cso', 'SKILL.md')} for the CSO skill instructions.
Run /cso on this repo (full daily audit, no flags).
describeIfSelected('CSO v3 — full audit', ['cso-full-audit'], () => {
test('/cso persists supported tenant-boundary findings with redacted evidence', async () => {
await withFixture({
'package.json': PACKAGE,
'.env': `PROVIDER_TOKEN=${CANARY}\n`,
'server.js': `const http = require('node:http');
const sessions = new Map([['alice-token', { tenant: 'alice' }], ['bob-token', { tenant: 'bob' }]]);
const invoices = [{ id: 'invoice-a', tenant: 'alice', total: 42 }, { id: 'invoice-b', tenant: 'bob', total: 950 }];
http.createServer((req, res) => {
const user = sessions.get((req.headers.authorization || '').replace('Bearer ', ''));
if (!user) { res.writeHead(401); return res.end('unauthorized'); }
const id = req.url.split('/').pop();
const invoice = invoices.find(item => item.id === id);
if (!invoice) { res.writeHead(404); return res.end('not found'); }
res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(invoice));
}).listen(3000);
`,
},undefined,async f=>{
const result = await runSkillTest({
testName: 'cso-full-audit',
runId: `cso-full-audit-${process.env.EVALS_RUN_ID ?? runId}-${process.pid}-${++captureSequence}`,
publicStreamDiagnostics: true,
prompt: prompt(f, ''), workingDirectory: f.repo,
env: { GSTACK_HOME: f.state }, maxTurns: 40, allowedTools: ['Bash', 'Read', 'Write', 'Grep', 'Glob'], timeout: CAPTURE_LONG_MS,
});
logCost('cso', result);
let passed = false;
try {
expect(result.exitReason).toBe('success');
expect(result.output).not.toContain(CANARY);
expect(JSON.stringify(result.transcript)).not.toContain(CANARY);
// Dedicated carve behavioral guard: a real audit must actually load its domain section.
expect(result.toolCalls.some(call => /Read|Bash/.test(call.tool)
&& JSON.stringify(call.input).includes('sections/audit-phases.md'))).toBe(true);
const report = requireReport(f);
const tenant = report.findings.filter(item => item.location.path === 'server.js' && /tenant|owner|authoriz|access control/i.test(`${item.rootCause} ${item.impact}`));
expect(tenant.length).toBeGreaterThan(0);
expect(tenant.every(item => item.trace.length > 0 && item.challenge.controls.length > 0)).toBe(true);
passed = result.browseErrors.length === 0;
} finally {
recordE2E(evalCollector, 'cso-full-audit', 'e2e-cso', result, { passed });
}
});
}, CAPTURE_LONG_MS + CAPTURE_CLEANUP_MS);
});
IMPORTANT:
- Do NOT use AskUserQuestion skip any interactive prompts.
- Focus on finding the planted vulnerabilities in this small repo.
- Produce the SECURITY FINDINGS table.
- Save the report to .gstack/security-reports/.`,
workingDirectory: csoDir,
maxTurns: 30,
allowedTools: ['Bash', 'Read', 'Write', 'Edit', 'Grep', 'Glob', 'Agent'],
timeout: CAPTURE_MS,
describeIfSelected('CSO v3 — diff mode', ['cso-diff-mode'], () => {
test('/cso --diff records its base and investigates changed security paths', async () => {
await withFixture({ 'package.json': PACKAGE, 'app.js': 'console.log("fixture baseline");\n' }, {
'webhook.js': `const http = require('node:http');
const payments = new Map();
http.createServer((req, res) => {
if (req.method !== 'POST' || req.url !== '/webhook/payment') { res.writeHead(404); return res.end(); }
let body = ''; req.on('data', chunk => body += chunk);
req.on('end', () => {
const event = JSON.parse(body);
payments.set(event.accountId, { plan: 'paid', amount: event.amount });
res.end('payment applied');
});
}).listen(3000);
`,
},async f=>{
const result = await runSkillTest({
testName: 'cso-diff-mode',
runId: `cso-diff-mode-${process.env.EVALS_RUN_ID ?? runId}-${process.pid}-${++captureSequence}`,
publicStreamDiagnostics: true,
prompt: prompt(f, '--diff'), workingDirectory: f.repo,
env: { GSTACK_HOME: f.state }, maxTurns: 40, allowedTools: ['Bash', 'Read', 'Write', 'Grep', 'Glob'], timeout: CAPTURE_LONG_MS,
});
logCost('cso', result);
let passed = false;
try {
expect(result.exitReason).toBe('success');
const report = requireReport(f);
expect(report.policy.diff).toBe(true);
expect(report.policy.base).toBe('main');
expect(report.source.baseCommit).toBe(git(f.repo, 'rev-parse', 'main').trim());
expect(report.findings.some(item => item.location.path === 'webhook.js' && /signature|authenticat|forg/i.test(`${item.rootCause} ${item.impact}`))).toBe(true);
expect(report.findings.every(item => item.location.path === 'webhook.js')).toBe(true);
passed = result.browseErrors.length === 0;
} finally {
recordE2E(evalCollector, 'cso-diff-mode', 'e2e-cso', result, { passed });
}
});
logCost('cso', result);
let passed = false;
try {
expect(result.exitReason).toBe('success');
// Should detect hardcoded API key
const output = result.output.toLowerCase();
expect(
output.includes('sk-') || output.includes('hardcoded') || output.includes('api key') || output.includes('api_key')
).toBe(true);
// Should detect .env tracked by git
expect(
output.includes('.env') && (output.includes('tracked') || output.includes('gitignore'))
).toBe(true);
// Should produce a findings table
expect(
output.includes('security findings') || output.includes('SECURITY FINDINGS')
).toBe(true);
}, CAPTURE_LONG_MS + CAPTURE_CLEANUP_MS);
});
// Should save a report
const reportDir = path.join(csoDir, '.gstack', 'security-reports');
const reportExists = fs.existsSync(reportDir);
if (reportExists) {
const reports = fs.readdirSync(reportDir).filter(f => f.endsWith('.json'));
expect(reports.length).toBeGreaterThanOrEqual(1);
describeIfSelected('CSO v3 — infra scope', ['cso-infra-scope'], () => {
test('/cso --infra finds an attacker-to-credential execution path', async () => {
await withFixture({ 'package.json': PACKAGE,
'.github/workflows/comment.yml': `name: comment automation
on:
issue_comment:
types: [created]
permissions:
contents: write
jobs:
reply:
runs-on: ubuntu-latest
steps:
- name: Handle untrusted comment
env:
GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
run: echo "\${{ github.event.comment.body }}"
`,
'Dockerfile': 'FROM node:22\nWORKDIR /app\nCOPY . .\nCMD ["node", "server.js"]\n',
},undefined,async f=>{
const result = await runSkillTest({
testName: 'cso-infra-scope',
runId: `cso-infra-scope-${process.env.EVALS_RUN_ID ?? runId}-${process.pid}-${++captureSequence}`,
publicStreamDiagnostics: true,
prompt: prompt(f, '--infra'), workingDirectory: f.repo,
env: { GSTACK_HOME: f.state }, maxTurns: 40, allowedTools: ['Bash', 'Read', 'Write', 'Grep', 'Glob'], timeout: CAPTURE_LONG_MS,
});
logCost('cso', result);
let passed = false;
try {
expect(result.exitReason).toBe('success');
const report = requireReport(f);
expect(report.policy.scope).toBe('infra');
const workflow=report.findings.find(item=>item.location.path==='.github/workflows/comment.yml');
expect(workflow).toBeDefined();
const chain=[workflow!.title,workflow!.rootCause,workflow!.attackerControl,workflow!.impact,workflow!.scenario,...workflow!.trace].join(' ');
expect(chain).toMatch(/github\.event\.comment\.body|issue comment body|comment body/i);
expect(chain).toMatch(/run(?: step|:)|shell|bash/i);
expect(chain).toMatch(/GITHUB_TOKEN|contents:\s*write|repository write/i);
// A missing USER directive is only a hardening lead without demonstrated attacker impact.
expect(report.findings.some(item => item.location.path === 'Dockerfile' && /critical|high/.test(item.severity))).toBe(false);
passed = result.browseErrors.length === 0;
} finally {
recordE2E(evalCollector, 'cso-infra-scope', 'e2e-cso', result, { passed });
}
passed = true;
} finally {
recordE2E(evalCollector, 'cso-full-audit', 'e2e-cso', result, { passed: passed && result.browseErrors.length === 0 });
}
}, CAPTURE_MS + CAPTURE_CLEANUP_MS);
});
describeIfSelected('CSO v2 — diff mode', ['cso-diff-mode'], () => {
let csoDiffDir: string;
beforeAll(() => {
csoDiffDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-cso-diff-'));
const run = (cmd: string, args: string[]) =>
spawnSync(cmd, args, { cwd: csoDiffDir, stdio: 'pipe', timeout: 5000 });
run('git', ['init', '-b', 'main']);
run('git', ['config', 'user.email', 'test@test.com']);
run('git', ['config', 'user.name', 'Test']);
// Clean initial commit
fs.writeFileSync(path.join(csoDiffDir, 'package.json'), JSON.stringify({
name: 'cso-diff-test', version: '1.0.0',
}, null, 2));
fs.writeFileSync(path.join(csoDiffDir, 'app.ts'), 'console.log("hello");\n');
run('git', ['add', '.']);
run('git', ['commit', '-m', 'initial']);
// Feature branch with a vuln
run('git', ['checkout', '-b', 'feat/add-webhook']);
fs.writeFileSync(path.join(csoDiffDir, 'webhook.ts'), `
import express from 'express';
const app = express();
// No signature verification!
app.post('/webhook/stripe', (req, res) => {
const event = req.body;
processPayment(event);
res.sendStatus(200);
});
`);
run('git', ['add', '.']);
run('git', ['commit', '-m', 'feat: add webhook']);
});
afterAll(() => {
try { fs.rmSync(csoDiffDir, { recursive: true, force: true }); } catch {}
});
test('/cso --diff scopes to branch changes', async () => {
const result = await runSkillTest({
testName: 'cso-diff-mode',
runId: `cso-diff-mode-${process.env.EVALS_RUN_ID ?? runId}-${process.pid}-${++captureSequence}`,
publicStreamDiagnostics: true,
prompt: `Read the file ${path.join(ROOT, 'cso', 'SKILL.md')} for the CSO skill instructions.
Run /cso --diff on this repo. The base branch is "main".
IMPORTANT:
- Do NOT use AskUserQuestion skip any interactive prompts.
- Focus on changes in the current branch vs main.
- The webhook.ts file was added on this branch it should be analyzed.`,
workingDirectory: csoDiffDir,
maxTurns: 40,
allowedTools: ['Bash', 'Read', 'Write', 'Edit', 'Grep', 'Glob', 'Agent'],
// 360s/40 turns: the v1.67 wave grew the audit session legitimately —
// transcript-verified, the agent finds the webhook vuln, spawns the
// verification subagent, and writes the report, then gets killed at
// ~215s in its CLOSING telemetry under the old 240s/25-turn budget.
// The full-audit sibling already runs at 300s.
timeout: 360_000,
});
logCost('cso', result);
let passed = false;
try {
expect(result.exitReason).toBe('success');
const output = result.output.toLowerCase();
// Should mention webhook and missing signature verification
expect(
output.includes('webhook') && (output.includes('signature') || output.includes('verify'))
).toBe(true);
passed = true;
} finally {
recordE2E(evalCollector, 'cso-diff-mode', 'e2e-cso', result, { passed: passed && result.browseErrors.length === 0 });
}
}, CAPTURE_LONG_MS);
});
describeIfSelected('CSO v2 — infra scope', ['cso-infra-scope'], () => {
let csoInfraDir: string;
beforeAll(() => {
csoInfraDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-cso-infra-'));
const run = (cmd: string, args: string[]) =>
spawnSync(cmd, args, { cwd: csoInfraDir, stdio: 'pipe', timeout: 5000 });
run('git', ['init', '-b', 'main']);
run('git', ['config', 'user.email', 'test@test.com']);
run('git', ['config', 'user.name', 'Test']);
// CI workflow with unpinned action
fs.mkdirSync(path.join(csoInfraDir, '.github', 'workflows'), { recursive: true });
fs.writeFileSync(path.join(csoInfraDir, '.github', 'workflows', 'ci.yml'), `
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: some-third-party/action@main
- run: echo "Building..."
`);
// Dockerfile running as root
fs.writeFileSync(path.join(csoInfraDir, 'Dockerfile'), `
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["node", "server.js"]
`);
run('git', ['add', '.']);
run('git', ['commit', '-m', 'initial']);
});
afterAll(() => {
try { fs.rmSync(csoInfraDir, { recursive: true, force: true }); } catch {}
});
test('/cso --infra runs infrastructure phases only', async () => {
const result = await runSkillTest({
testName: 'cso-infra-scope',
runId: `cso-infra-scope-${process.env.EVALS_RUN_ID ?? runId}-${process.pid}-${++captureSequence}`,
publicStreamDiagnostics: true,
prompt: `Read the file ${path.join(ROOT, 'cso', 'SKILL.md')} for the CSO skill instructions.
Run /cso --infra on this repo. This should run infrastructure-only phases (0-6, 12-14).
IMPORTANT:
- Do NOT use AskUserQuestion skip any interactive prompts.
- This is a TINY repo with only 3 files: .github/workflows/ci.yml, Dockerfile, and package.json. Do NOT waste turns exploring just read those files directly and audit them.
- The Dockerfile has no USER directive (runs as root). The CI workflow uses an unpinned third-party GitHub Action (some-third-party/action@main).
- Focus on infrastructure findings, NOT code-level OWASP scanning.
- Skip the preamble (gstack-update-check, telemetry, etc.) go straight to the audit.
- Do NOT use the Agent tool for exploration or verification read the files yourself. This repo is too small to need subagents.`,
workingDirectory: csoInfraDir,
maxTurns: 30,
allowedTools: ['Bash', 'Read', 'Write', 'Edit', 'Grep', 'Glob'],
timeout: CAPTURE_LONG_MS,
});
logCost('cso', result);
let passed = false;
try {
expect(result.exitReason).toBe('success');
const output = result.output.toLowerCase();
// Should mention unpinned action or Dockerfile issues
expect(
output.includes('unpinned') || output.includes('third-party') ||
output.includes('user directive') || output.includes('root')
).toBe(true);
passed = true;
} finally {
recordE2E(evalCollector, 'cso-infra-scope', 'e2e-cso', result, { passed: passed && result.browseErrors.length === 0 });
}
}, CAPTURE_LONG_MS + CAPTURE_CLEANUP_MS);
});
+31 -5
View File
@@ -49,6 +49,28 @@ function readShipUnion(): string {
return readSkillUnion('ship');
}
describe('CSO host permission boundary', () => {
test('grants no raw source, mutation, search, question, or Agent tools', () => {
const content = fs.readFileSync(path.join(ROOT, 'cso', 'SKILL.md'), 'utf-8');
const fmEnd = content.indexOf('\n---', 4);
const frontmatter = Bun.YAML.parse(content.slice(4, fmEnd)) as Record<string, unknown>;
const allowed = frontmatter['allowed-tools'];
expect(allowed).toEqual([
'Bash(~/.claude/skills/gstack/bin/gstack-cso-launcher *)',
'Bash(~/.claude/skills/gstack/bin/gstack-cso-launcher.exe *)',
]);
for (const broad of ['Bash', 'Read', 'Grep', 'Glob', 'Write', 'Agent', 'WebSearch', 'AskUserQuestion']) expect(allowed).not.toContain(broad);
});
test('retains helper-only source access, sequential challenge, and honest host containment', () => {
const content = readSkillUnion('cso');
expect(content).toContain('Never use host `Read`/`Glob`/`Grep`');
expect(content).toContain('sequential challenge; independent agent unavailable');
expect(content).toContain('Do not request broader tool access solely to obtain an independent reviewer.');
expect(content).toContain('Containment does not sandbox the host agent or kernel.');
});
});
function readCodexSkillUnion(skill: string): string {
const dir = path.join(CODEX_OUT, '.agents', 'skills', `gstack-${skill}`);
const sections = path.join(dir, 'sections');
@@ -58,7 +80,6 @@ function readCodexSkillUnion(skill: string): string {
.map(file => '\n' + fs.readFileSync(path.join(sections, file), 'utf-8')).join('') : '');
}
describe('SKILL.md command validation', () => {
// P2 (v1.2.0): the top-level gstack skill is a pure ROUTER, not the browse
// skill. The browse body lives only in browse/SKILL.md now. This regression
@@ -333,7 +354,8 @@ describe('Update check preamble', () => {
'benchmark/SKILL.md',
'land-and-deploy/SKILL.md',
'setup-deploy/SKILL.md',
'cso/SKILL.md',
// CSO intentionally uses a private startup instead of the shared update,
// session, learning, checkpoint, and telemetry PREAMBLE.
];
for (const skill of skillsWithUpdateCheck) {
@@ -698,7 +720,8 @@ describe('v0.4.1 preamble features', () => {
'canary/SKILL.md',
'land-and-deploy/SKILL.md',
'setup-deploy/SKILL.md',
'cso/SKILL.md',
// CSO's private startup intentionally omits the generic AUQ/session/
// escalation PREAMBLE; its helper owns readiness and terminal state.
];
const skillsWithPreamble = [...tier1Skills, ...tier2PlusSkills];
@@ -964,7 +987,9 @@ describe('Completeness Principle in generated SKILL.md files', () => {
'design-review/SKILL.md',
'design-consultation/SKILL.md',
'document-release/SKILL.md',
'cso/SKILL.md', ];
// CSO reports complete/partial/not-assessed from its own evidence contract
// and must not inherit the shared numerical completeness rubric.
];
for (const skill of skillsWithPreamble) {
test(`${skill} contains Completeness Principle section`, () => {
@@ -975,7 +1000,8 @@ describe('Completeness Principle in generated SKILL.md files', () => {
}
test('Completeness Principle keeps compact scoring guidance in tier 2+ skills', () => {
const content = fs.readFileSync(path.join(ROOT, 'cso', 'SKILL.md'), 'utf-8');
// CSO is intentionally exempt; use a regular tier 2+ PREAMBLE consumer.
const content = fs.readFileSync(path.join(ROOT, 'review', 'SKILL.md'), 'utf-8');
expect(content).toContain('Completeness: X/10');
expect(content).toContain('10 = all edge cases');
expect(content).toContain('Note: options differ in kind, not coverage');
+16
View File
@@ -123,6 +123,22 @@ describe('test-free-shards: Windows curation', () => {
expect(reason.length).toBeGreaterThan(0);
}
});
test('excludes POSIX CSO helper suites while retaining portable image metadata coverage', () => {
const posixOnly = [
'test/cso-preparation-adversarial.test.ts',
'test/cso-preparation-container.test.ts',
'test/cso-preparation-executor.test.ts',
'test/cso-scanner-cli.test.ts',
'test/cso-verification-cleanup.test.ts',
'test/cso-witness.test.ts',
];
const portable = ['test/cso-image-provisioning.test.ts', 'test/cso-public-ghcr.test.ts'];
const result = curateWindowsSafe([...posixOnly, ...portable], ROOT);
expect(result.safe).toEqual(portable);
expect(result.excluded.map(({ file }) => file).sort()).toEqual(posixOnly.sort());
for (const { reason } of result.excluded) expect(reason).toMatch(/Linux|POSIX|Windows/);
});
});
describe('test-free-shards: sharding', () => {