v1.88.1.0 fix: harden credential boundaries and owned state (#2942)

* fix(settings): preserve symlinked settings targets

Resolve the selected target for locking, mutation, backup, and rollback; refuse target changes and preserve private modes. Addresses #2830.

* fix(redact): bind masking to original detected spans

Inspired by #2929's anchored-span diagnosis; independently implemented using normalization offsets. Addresses #2930 and the relocation portion of #2912 without changing detection sensitivity.

* fix(evals): exclude operator credentials from prefix admission

Adapts the credential-suffix screen proposed in #2636, with real launched-child regression coverage and deliberate provider-auth exceptions.

* fix(artifacts): retain custom allowlist rules on reinitialization

Preserve the exact user-owned suffix and publish only a successfully assembled replacement. Independently implements the repair reported in #2907.

* test(cso): verify exact masked reads and unmaskable payload refusal

* fix(cso): preserve exact filesystem identities through lease recovery

Preserve 64-bit device/inode identity and nanosecond race checks. Add native NTFS lifecycle coverage for #2927; retain ambiguous legacy-state refusal without claiming Windows PID-reuse recovery is resolved.

* fix(redact): bind pre-push scans to destination and preserve seam context

Uses #2935 (bd07318) as source evidence for push-target range and slice-overlap defects. Independently implemented; no cherry-pick or release metadata adoption.

* test(ci): gate native agent ownership and settings links on macOS

* fix(browse): bind agent lifetimes and cleanup to owned generations

Uses #2931 by Chris Hutton / Claude Fable 5.1 as attributed design input; independently implemented without broad sweeps or copied code. Keep uncertain children and locks rather than deleting foreign state.

* test(ci): include concurrent shutdown controls in the native macOS gate

* v1.88.1.0 fix: harden credential boundaries and owned state

* fix(redact): preserve target provenance and scan boundary semantics

* test(artifacts): read managed rules from atomic allowlist assembly

* fix: preserve native exit observations and fixture prerequisites

* fix: preserve UTF-16 offsets through redaction normalization
This commit is contained in:
Garry Tan
2026-09-23 08:54:53 -04:00
committed by GitHub
parent 636175d349
commit b9706f3635
42 changed files with 2719 additions and 339 deletions
+27 -1
View File
@@ -73,7 +73,33 @@ describe('compiled CSO command workflow',()=>{
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('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).toBe(0);expect(result.stdout.trim()).toBe('TOKEN=<REDACTED-env.kv+github.pat>');expect(result.stderr).toBe('');expect(result.stdout+result.stderr).not.toContain('ghp_');expect(result.stdout+result.stderr).not.toContain(CREDENTIAL_CANARY);expect(fs.existsSync(path.join(state,'security','cso',run.repoId,run.runId,'snapshot','.env'))).toBe(false);});
test('supplementary NFKC expansion cannot displace compiled reader redaction',()=>{
const file=path.join(repo,'.env');
expect(fs.realpathSync(file)).toBe(path.join(fs.realpathSync(repo),'.env'));
const original=fs.readFileSync(file),prefix='\uFA6C'.repeat(64)+'\n',suffix='\n'+'z'.repeat(256);
const value=['8Fk2pQ9vXz4wL7mN','3rT6yB1cD5eG0hJq'].join('');
try{
fs.writeFileSync(file,`${prefix}DB_PASSWORD=${value}${suffix}`);
const started=command(['start','--repo',repo,'--scope','auth','--offline']);expect(started.status).toBe(0);
const run=JSON.parse(started.stdout),result=command(['read',run.runId,'.env']);
expect(result.status).toBe(0);expect(result.stderr).toBe('');
expect(result.stdout.trimEnd()).toBe(`${prefix}DB_PASSWORD=<REDACTED-env.kv>${suffix}`);
expect(result.stdout+result.stderr).not.toContain(value);
expect(fs.existsSync(path.join(state,'security','cso',run.repoId,run.runId,'snapshot','.env'))).toBe(false);
}finally{fs.writeFileSync(file,original);}
});
test('marker-only credential payloads remain unavailable to the compiled reader',()=>{
const file=path.join(repo,'.env'),original=fs.readFileSync(file),body=['-----BEGIN ','PRIVATE KEY-----\n','synthetic-key-body\n','-----END ','PRIVATE KEY-----\n'].join('');
try{
fs.writeFileSync(file,body);
const started=command(['start','--repo',repo,'--scope','auth','--offline']);expect(started.status).toBe(0);
const run=JSON.parse(started.stdout),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('synthetic-key-body');
const dir=path.join(state,'security','cso',run.repoId,run.runId);
expect(fs.existsSync(path.join(dir,'readable','.env'))).toBe(false);expect(fs.existsSync(path.join(dir,'snapshot','.env'))).toBe(false);
}finally{fs.writeFileSync(file,original);}
});
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);});