mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-27 07:01:54 +02:00
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:
@@ -310,6 +310,72 @@ describe('CSO native Windows build contract', () => {
|
||||
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);
|
||||
}, 120_000);
|
||||
|
||||
test('NTFS high file IDs survive repeated native commands and ambiguous old decisions remain blocked', () => {
|
||||
const repository=path.join(temporary,'lease lifecycle repository'),profile=path.join(temporary,'lease lifecycle 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("fixture")\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};
|
||||
const command=(args:string[])=>spawnSync(actual,args,{cwd:repository,encoding:'utf8',env,timeout:30_000}),submission=path.join(profile,'submission.json');
|
||||
fs.writeFileSync(submission,'{}\n');
|
||||
for(let i=0;i<3;i++){
|
||||
const started=command(['start','--repo',repository,'--offline']);expectSuccessfulProcess(started,'gstack-cso start');
|
||||
const run=JSON.parse(started.stdout),dir=path.join(profile,'.gstack','security','cso',run.repoId,run.runId),leases=path.join(dir,'.mutation-lock-leases');
|
||||
const initialized=command(['resume',run.runId]);expectSuccessfulProcess(initialized,'gstack-cso initialize mutation lease');
|
||||
expect(fs.realpathSync(leases).startsWith(fs.realpathSync(profile)+path.sep)).toBe(true);
|
||||
if(i===0){
|
||||
const churn=path.join(leases,'churn');let inode=0n;
|
||||
for(let attempt=0;attempt<1024;attempt++){
|
||||
fs.writeFileSync(churn,'x');inode=fs.lstatSync(churn,{bigint:true}).ino;fs.unlinkSync(churn);
|
||||
if(inode>BigInt(Number.MAX_SAFE_INTEGER))break;
|
||||
}
|
||||
expect(inode).toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER));
|
||||
}
|
||||
const inspected=command(['inspect',run.runId]);expectSuccessfulProcess(inspected,'gstack-cso inspect');
|
||||
expect(JSON.parse(inspected.stdout).report.status).toBe('running');
|
||||
const read=command(['read',run.runId,'app.js']);expectSuccessfulProcess(read,'gstack-cso read');expect(read.stdout).toContain('fixture');
|
||||
const history=command(['history',run.runId]);expectSuccessfulProcess(history,'gstack-cso history');
|
||||
const submitted=command(['submit',run.runId,submission]);expectSuccessfulProcess(submitted,'gstack-cso submit');
|
||||
const resumed=command(['resume',run.runId]);expectSuccessfulProcess(resumed,'gstack-cso resume');
|
||||
const finished=command(['finish',run.runId]);expectSuccessfulProcess(finished,'gstack-cso finish');
|
||||
expect(JSON.parse(finished.stdout).completeness).not.toBe('complete');
|
||||
const report=JSON.parse(fs.readFileSync(path.join(dir,'report.json'),'utf8'));
|
||||
expect(report.coverage.some((entry:any)=>entry.status==='not_assessed')).toBe(true);
|
||||
expect(fs.readdirSync(leases)).toEqual([]);
|
||||
if(i!==2)continue;
|
||||
const token='d'.repeat(32),candidate=path.join(leases,`${token}.json`),decision=path.join(leases,`${token}.decision`);
|
||||
for(const rounded of [false,true]){
|
||||
let selected:string|undefined;
|
||||
for(let batch=0;batch<16&&!selected;batch++){
|
||||
const paths:string[]=[];
|
||||
for(let index=0;index<64;index++){
|
||||
const file=path.join(leases,`fixture-${batch}-${index}`);
|
||||
fs.writeFileSync(file,JSON.stringify({pid:2147483647,token,createdAt:0})+'\n');paths.push(file);
|
||||
const inode=fs.lstatSync(file,{bigint:true}).ino;
|
||||
if(!selected&&inode>BigInt(Number.MAX_SAFE_INTEGER)&&String(Number(inode))!==String(inode))selected=file;
|
||||
}
|
||||
for(const file of paths){if(file===selected)fs.renameSync(file,candidate);else fs.unlinkSync(file);}
|
||||
}
|
||||
expect(selected).toBeDefined();
|
||||
const stat=fs.lstatSync(candidate,{bigint:true}),record={schemaVersion:1,token,kind:'ticket',ticket:'0000000000000001',candidateDev:String(stat.dev),candidateIno:rounded?String(Number(stat.ino)):String(stat.ino),ownerPid:2147483647,ownerCreatedAt:0,publisherPid:2147483647,createdAt:0};
|
||||
fs.writeFileSync(decision,JSON.stringify(record)+'\n');
|
||||
const result=command(['resume',run.runId]);expect(result.status).not.toBe(0);
|
||||
if(rounded){
|
||||
expect(record.candidateIno).not.toBe(String(stat.ino));expect(result.stderr).toContain('UNSAFE_PATH');
|
||||
expect(fs.existsSync(candidate)).toBe(true);expect(fs.existsSync(decision)).toBe(true);
|
||||
fs.unlinkSync(decision);fs.unlinkSync(candidate);
|
||||
}else{
|
||||
expect(result.stderr).toContain('INVALID_SCHEMA');
|
||||
expect(fs.existsSync(candidate)).toBe(false);expect(fs.existsSync(decision)).toBe(false);
|
||||
}
|
||||
}
|
||||
const next=command(['start','--repo',repository,'--offline']);expectSuccessfulProcess(next,'gstack-cso start after legacy state');
|
||||
expect(JSON.parse(next.stdout).runId).not.toBe(run.runId);
|
||||
}
|
||||
}, 180_000);
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user