mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-06 08:54:04 +01:00
fix(cso): refine Windows native diagnostics
This commit is contained in:
1 parent
257321b70b
commit
27c04e632b
3 files changed
+20
-4
No files matched your search
+3
-1
@@ -205,8 +205,10 @@ export async function git(repo: string, args: string[], home: string): Promise<s
|
||||
// content. Name only the fixed helper-owned operation and bounded process
|
||||
// outcome so native failures are actionable without exposing either.
|
||||
const knownOperations=new Set(['rev-parse','symbolic-ref','ls-files','ls-tree','log','merge-base']),operation=args.find(value=>knownOperations.has(value))??'metadata',
|
||||
phase=operation==='rev-parse'&&args.includes('--show-object-format')?'object-format':operation==='rev-parse'&&args.includes('--is-inside-work-tree')?'worktree-probe':operation,
|
||||
reason=/not a git repository|outside repository/i.test(result.stderr)?'repository unavailable':/dubious ownership/i.test(result.stderr)?'repository ownership rejected':/(?:bad|invalid|unable to read).*config|config (?:error|file)/i.test(result.stderr)?'configuration rejected':/unknown option|unknown switch|unrecognized option|usage:/i.test(result.stderr)?'unsupported invocation':/(?:cannot|could not|unable to) (?:chdir|change directory)|no such file or directory/i.test(result.stderr)?'path unavailable':'request rejected',
|
||||
outcome=result.timedOut?'timed out':result.truncated?'exceeded the output limit':`exited ${result.code}`;
|
||||
throw new CsoError('MISSING_INPUT',`Could not read bounded Git metadata: ${operation} ${outcome}; source may not be a Git repository`);
|
||||
throw new CsoError('MISSING_INPUT',`Could not read bounded Git metadata: ${phase} ${outcome} (${reason}); source may not be a Git repository`);
|
||||
}
|
||||
return result.stdout;
|
||||
}
|
||||
@@ -36,10 +36,20 @@ describe('CSO Git metadata hardening',()=>{
|
||||
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'});
|
||||
|
||||
@@ -16,11 +16,15 @@ function trustedGit():string{
|
||||
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={dev:bigint;ino:bigint};
|
||||
function fileIdentity(file:string):FileIdentity{const stat=fs.statSync(file,{bigint:true});return{dev:stat.dev,ino:stat.ino};}
|
||||
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 current=fs.statSync(String(candidate),{bigint:true});return current.isFile()&¤t.dev===expected.dev&¤t.ino===expected.ino;}catch{return false;}
|
||||
try{const file=String(candidate),current=fs.statSync(file,{bigint:true});return canonicalFile(file)===expected.path&¤t.dev===expected.dev&¤t.ino===expected.ino;}catch{return false;}
|
||||
}
|
||||
|
||||
function fixture(){
|
||||
|
||||
Reference in new issue
Block a user