Files
ycode/scripts/sandbox-git-remote-regression.ts
T

224 lines
25 KiB
TypeScript

import assert from 'node:assert/strict'
import { promises as fs } from 'node:fs'
import { createServer, type ServerResponse, type IncomingMessage } from 'node:http'
import { createServer as createTlsServer } from 'node:https'
import { spawn, execFileSync } from 'node:child_process'
import { generateKeyPairSync } from 'node:crypto'
import { createRequire } from 'node:module'
import { join, resolve } from 'node:path'
import { configureSandbox } from '../src/main/sandbox/configuration'
import { gitHostAction } from '../src/main/sandbox/git-host-service'
import { gitRemoteAction, gitRemoteOperation, reconcileGitRemote } from '../src/main/sandbox/git-remote'
import { importGitCredential, listGitCredentials, removeGitCredential, gitRemoteUrl } from '../src/main/sandbox/git-credentials'
import { executeTool } from '../src/main/agent/tools'
import { workspaceAction } from '../src/main/sandbox/workspace-service'
import { readSandboxPolicy, updateSandboxPolicy } from '../src/main/sandbox/policy-store'
import { gitRemoteEndpoint } from '../src/main/sandbox/git-credentials'
const require = createRequire(import.meta.url)
const { Server: SshServer } = require(resolve('scripts/fixtures/git-transport/node_modules/ssh2'))
const selfsigned = require(resolve('scripts/fixtures/git-transport/node_modules/selfsigned'))
const fixture = await fs.mkdtemp(resolve('.tmp/sandbox-git-remote-')), workspace = join(fixture, 'project \u4e2d\u6587 ^'), stateRoot = join(fixture, 'state'), remote = join(fixture, 'remote.git')
await fs.mkdir(workspace); await fs.mkdir(stateRoot)
configureSandbox({ stateRoot, resourceDirectory: resolve('resources/sandbox/win32-x64') })
const git = resolve('resources/sandbox/win32-x64/git/files/git.exe'), tool = resolve('resources/sandbox/win32-x64/git/files')
const environment = { SystemRoot: process.env.SystemRoot, PATH: `${tool};${process.env.SystemRoot}\\System32`, GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: 'NUL', GIT_CONFIG_SYSTEM: 'NUL', GIT_TERMINAL_PROMPT: '0' }
const prefix = ['-c', 'core.hooksPath=NUL', '-c', 'core.fsmonitor=false', '-c', 'user.name=Fixture', '-c', 'user.email=fixture@example.invalid']
const hostGit = (...args: string[]): string => execFileSync(git, [...prefix, ...args], { cwd: workspace, env: environment, encoding: 'utf8', windowsHide: true })
const checks: Array<{ label: string; pass: boolean }> = []
function check(label: string, condition: unknown): void { checks.push({ label, pass: !!condition }); assert.ok(condition, label); console.log(`PASS ${label}`) }
let approvals = 0, httpRequests = 0, sshConnections = 0, requireAuth = false, disconnectPush = false, holdResponses = false
let onHeldRequest: (() => void) | undefined
const heldResponses = new Set<ServerResponse>(), remotePaths = new Map([['/repo.git', remote]])
const context = { workspaceRoot: workspace, sessionId: 'remote-session', runId: 'remote-run', toolCallId: 'remote-call', approve: async () => { approvals++; return true } }
const run = (argv: string[]) => executeTool('sandbox_git', { action: 'run', argv }, context)
const children = new Set<ReturnType<typeof spawn>>()
function serve(request: IncomingMessage, response: ServerResponse): void {
httpRequests++
if (holdResponses) { heldResponses.add(response); response.once('close', () => heldResponses.delete(response)); onHeldRequest?.(); return }
if (requireAuth && request.headers.authorization !== `Basic ${Buffer.from('fixture:DUMMY_GIT_TOKEN_97832').toString('base64')}`) { response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="fixture"' }); response.end(); return }
const url = new URL(request.url!, 'http://fixture.invalid'), service = request.method === 'GET' ? url.searchParams.get('service') : url.pathname.split('/').at(-1)
const repository = remotePaths.get('/' + url.pathname.split('/')[1])
if (!repository || !['git-upload-pack', 'git-receive-pack'].includes(service ?? '')) { response.writeHead(404); response.end(); return }
const advertise = request.method === 'GET', args = [...prefix, service!.slice(4), '--stateless-rpc', ...(advertise ? ['--advertise-refs'] : []), repository]
const child = spawn(git, args, { env: environment, windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'] }); children.add(child)
child.stderr.on('data', () => undefined); child.on('error', error => response.destroy(error)); child.on('close', () => children.delete(child))
response.writeHead(200, { 'Content-Type': `application/x-${service}-${advertise ? 'advertisement' : 'result'}`, 'Cache-Control': 'no-cache' })
if (advertise) { const header = Buffer.from(`# service=${service}\n`); response.write(Buffer.from((header.length + 4).toString(16).padStart(4, '0'))); response.write(header); response.write('0000'); child.stdin.end() }
else request.pipe(child.stdin)
if (disconnectPush && service === 'git-receive-pack' && !advertise) { child.stdout.resume(); child.once('close', () => response.destroy()) }
else child.stdout.pipe(response)
child.stdin.on('error', () => undefined)
}
const http = createServer(serve), pems = selfsigned.generate([{ name: 'commonName', value: '127.0.0.1' }], { days: 2, keySize: 2048, algorithm: 'sha256', extensions: [
{ name: 'basicConstraints', cA: true }, { name: 'keyUsage', keyCertSign: true, digitalSignature: true, keyEncipherment: true },
{ name: 'extKeyUsage', serverAuth: true }, { name: 'subjectAltName', altNames: [{ type: 7, ip: '127.0.0.1' }] }
] })
const https = createTlsServer({ key: pems.private, cert: pems.cert }, serve)
const listen = (server: any): Promise<number> => new Promise((done, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', () => done(server.address().port)) })
function sshPublic(jwk: JsonWebKey): Buffer {
const field = (data: Buffer): Buffer => { const header = Buffer.alloc(4); header.writeUInt32BE(data.length); return Buffer.concat([header, data]) }
const integer = (text: string): Buffer => { const data = Buffer.from(text, 'base64url'); return field(data[0] & 128 ? Buffer.concat([Buffer.from([0]), data]) : data) }
return Buffer.concat([field(Buffer.from('ssh-rsa')), integer(jwk.e!), integer(jwk.n!)])
}
const hostKey = generateKeyPairSync('rsa', { modulusLength: 2048 }), clientKey = generateKeyPairSync('rsa', { modulusLength: 2048 })
const hostPem = hostKey.privateKey.export({ type: 'pkcs1', format: 'pem' }).toString(), clientPem = clientKey.privateKey.export({ type: 'pkcs1', format: 'pem' }).toString()
const clientPublic = sshPublic(clientKey.publicKey.export({ format: 'jwk' })), clients = new Set<any>()
const ssh = new SshServer({ hostKeys: [hostPem] }, (client: any) => {
sshConnections++; clients.add(client); client.on('error', () => undefined); client.on('close', () => clients.delete(client))
client.on('authentication', (auth: any) => { if (auth.method === 'publickey' && auth.key.data.equals(clientPublic)) auth.accept(); else auth.reject() })
client.on('ready', () => client.on('session', (accept: any) => accept().on('exec', (accept: any, reject: any, info: any) => {
const match = /^git-(upload-pack|receive-pack) '\/repo\.git'$/.exec(info.command)
if (!match) { reject(); return }
const stream = accept(), child = spawn(git, [...prefix, match[1], remote], { env: environment, windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'] })
children.add(child); stream.pipe(child.stdin); child.stdout.pipe(stream, { end: false }); child.stderr.pipe(stream.stderr, { end: false }); child.stdin.on('error', () => undefined)
child.once('close', code => { children.delete(child); stream.exit(code ?? 1); stream.end() })
})))
})
try {
hostGit('init', '-q', '--template=', '--initial-branch=main'); await fs.writeFile(join(workspace, 'file.txt'), 'remote-base\n'); hostGit('add', '.'); hostGit('commit', '-q', '-m', 'remote-base')
hostGit('init', '--bare', '-q', '--template=', '--initial-branch=main', remote)
hostGit('-c', 'protocol.file.allow=always', 'push', '-q', remote, 'main')
await gitHostAction('import', context)
const httpPort = await listen(http), tlsPort = await listen(https), sshPort = await listen(ssh)
const httpUrl = `http://127.0.0.1:${httpPort}/repo.git`, httpsUrl = `https://127.0.0.1:${tlsPort}/repo.git`, sshUrl = `ssh://fixture@127.0.0.1:${sshPort}/repo.git`
const beforeDeny = httpRequests
let result = await gitRemoteAction({ action: 'ls-remote', url: httpUrl }, { ...context, approve: async () => false })
check('remote rejection sends no network requests', result.state === 'denied' && httpRequests === beforeDeny)
result = await gitRemoteAction({ action: 'ls-remote', url: httpUrl }, context)
check('approved smart HTTP discovery uses the bundled transport helper', result.state === 'completed' && result.stdout!.includes('refs/heads/main'))
result = await gitRemoteAction({ action: 'fetch', url: httpUrl, branch: 'main' }, context)
check('approved fetch persists remote tracking refs without host metadata writes', result.state === 'completed' && !!result.revision && !hostGit('branch', '-r').includes('origin/main'))
check('isolated commands observe the fetched tracking refs', (await run(['log', '-1', '--oneline', 'refs/remotes/origin/main'])).content.includes('remote-base'))
result = await gitRemoteAction({ action: 'ls-remote', url: httpsUrl }, context)
check('HTTPS refuses an untrusted self-signed server without an explicit CA', result.state === 'failed' && /certificate|SSL|schannel/i.test(result.reason ?? ''))
const credential = await importGitCredential(httpsUrl, { type: 'https', username: 'fixture', secret: 'DUMMY_GIT_TOKEN_97832', caCertificate: pems.cert })
requireAuth = true
result = await gitRemoteAction({ action: 'ls-remote', url: httpsUrl, credentialId: credential.id }, context)
check('HTTPS authenticates using an exact-remote DPAPI credential and verifies the explicit CA', result.state === 'completed' && result.stdout!.includes('refs/heads/main'))
const stored = await fs.readFile(join(stateRoot, 'git-credentials', `${credential.id}.json`), 'utf8')
check('credential records and result queries contain no plaintext token', !stored.includes('DUMMY_GIT_TOKEN_97832') && !JSON.stringify(await gitRemoteOperation(context, result.operationId)).includes('DUMMY_GIT_TOKEN_97832'))
check('credential listing exposes metadata only', (await listGitCredentials()).some(value => value.id === credential.id) && !JSON.stringify(await listGitCredentials()).includes('DUMMY_GIT_TOKEN_97832'))
const beforeScope = httpRequests
await assert.rejects(gitRemoteAction({ action: 'ls-remote', url: httpsUrl.replace('repo.git', 'other.git'), credentialId: credential.id }, context), /scoped/)
check('credential scope mismatch is rejected before any connection', httpRequests === beforeScope)
const revokedCredential = await importGitCredential(httpsUrl, { type: 'https', username: 'fixture', secret: 'DUMMY_REVOKED_TOKEN', caCertificate: pems.cert })
const beforeRevoke = httpRequests
result = await gitRemoteAction({ action: 'ls-remote', url: httpsUrl, credentialId: revokedCredential.id }, { ...context, approve: async () => { await removeGitCredential(revokedCredential.id); return true } })
check('revoking a credential during approval prevents the first connection', result.state === 'failed' && httpRequests === beforeRevoke)
requireAuth = false
await run(['commit', '--allow-empty', '-m', 'remote-push-commit'])
result = await gitRemoteAction({ action: 'push', url: httpUrl, refspecs: ['HEAD:refs/heads/feature'] }, context)
check('approved atomic push publishes exact intended refs', result.state === 'completed' && result.writeback === 'remote-committed' && hostGit('--git-dir=' + remote, 'log', '-1', '--oneline', 'feature').includes('remote-push-commit'))
check('remote result survives a fresh owned query', (await gitRemoteOperation(context, result.operationId)).writeback === 'remote-committed')
result = await gitRemoteAction({ action: 'push', url: httpUrl, refspecs: ['HEAD:refs/heads/dry-run'], dryRun: true }, context)
check('push dry-run performs negotiation without publishing a ref', result.state === 'completed' && result.writeback === 'not-applied' && !hostGit('--git-dir=' + remote, 'for-each-ref', 'refs/heads/dry-run').trim())
const remoteTree = hostGit('--git-dir=' + remote, 'rev-parse', 'main^{tree}').trim()
const divergent = hostGit('--git-dir=' + remote, 'commit-tree', remoteTree, '-m', 'divergent-remote').trim()
hostGit('--git-dir=' + remote, 'update-ref', 'refs/heads/feature', divergent)
result = await gitRemoteAction({ action: 'push', url: httpUrl, refspecs: ['HEAD:refs/heads/feature'], forceWithLease: { 'refs/heads/feature': divergent } }, context)
check('force-with-lease permits a non-fast-forward update only with the approved old object', result.state === 'completed' && hostGit('--git-dir=' + remote, 'rev-parse', 'feature').trim() !== divergent)
const protectedFeature = hostGit('--git-dir=' + remote, 'rev-parse', 'feature').trim()
await run(['commit', '--allow-empty', '-m', 'stale-lease-attempt'])
result = await gitRemoteAction({ action: 'push', url: httpUrl, refspecs: ['HEAD:refs/heads/feature'], forceWithLease: { 'refs/heads/feature': divergent } }, context)
check('a stale force-with-lease is refused without changing the remote ref', result.state !== 'completed' && /stale|rejected/i.test(result.reason ?? '') && hostGit('--git-dir=' + remote, 'rev-parse', 'feature').trim() === protectedFeature)
result = await gitRemoteAction({ action: 'push', url: httpUrl, refspecs: [':refs/heads/feature'] }, context)
check('explicit approved deletion removes only the named remote ref', result.state === 'completed' && !hostGit('--git-dir=' + remote, 'for-each-ref', 'refs/heads/feature').trim() && !!hostGit('--git-dir=' + remote, 'for-each-ref', 'refs/heads/main').trim())
await run(['remote', 'add', 'origin', httpUrl]); await run(['config', 'branch.main.remote', 'origin']); await run(['config', 'branch.main.merge', 'refs/heads/main'])
await gitRemoteAction({ action: 'push', url: httpUrl, refspecs: ['HEAD:refs/heads/main'] }, context)
const advanceRemote = (message: string): string => {
const parent = hostGit('--git-dir=' + remote, 'rev-parse', 'main').trim(), next = hostGit('--git-dir=' + remote, 'commit-tree', remoteTree, '-p', parent, '-m', message).trim()
hostGit('--git-dir=' + remote, 'update-ref', 'refs/heads/main', next); return next
}
const fastForward = advanceRemote('remote-fast-forward')
let pulled = await executeTool('sandbox_git', { action: 'pull' }, context)
check('pull derives the current upstream and fast-forwards only the private draft by default', pulled.ok && (await run(['rev-parse', 'HEAD'])).content.includes(fastForward) && !hostGit('log', '-1', '--oneline').includes('remote-fast-forward'))
await run(['commit', '--allow-empty', '-m', 'local-divergence'])
const privateHead = (await run(['rev-parse', 'HEAD'])).content
advanceRemote('remote-divergence')
pulled = await executeTool('sandbox_git', { action: 'pull' }, context)
check('default pull refuses divergent history and preserves the local commit', !pulled.ok && (await run(['rev-parse', 'HEAD'])).content === privateHead)
pulled = await executeTool('sandbox_git', { action: 'pull', pullStrategy: 'rebase' }, context)
check('explicit pull rebase integrates divergence inside the isolated draft', pulled.ok && (await run(['log', '-3', '--oneline'])).content.includes('remote-divergence'))
await run(['commit', '--allow-empty', '-m', 'local-merge-divergence']); advanceRemote('remote-merge-divergence')
pulled = await executeTool('sandbox_git', { action: 'pull', pullStrategy: 'merge' }, context)
check('explicit pull merge retains both histories with a real merge commit', pulled.ok && /[a-f0-9]{40} [a-f0-9]{40}/.test((await run(['log', '-1', '--format=%P'])).content))
const sshCredential = await importGitCredential(sshUrl, { type: 'ssh', privateKey: clientPem, knownHosts: `[127.0.0.1]:${sshPort} ssh-rsa ${sshPublic(hostKey.publicKey.export({ format: 'jwk' })).toString('base64')}\n` })
result = await gitRemoteAction({ action: 'ls-remote', url: sshUrl, credentialId: sshCredential.id }, context)
check('bundled SSH authenticates with an explicit key and pinned host key', result.state === 'completed' && result.stdout!.includes('refs/heads/main') && sshConnections > 0)
result = await gitRemoteAction({ action: 'fetch', url: sshUrl, credentialId: sshCredential.id, branch: 'main' }, context)
check('SSH fetch persists objects through the same controlled mirror', result.state === 'completed' && !!result.revision)
result = await gitRemoteAction({ action: 'push', url: sshUrl, credentialId: sshCredential.id, refspecs: ['HEAD:refs/heads/ssh-pushed'] }, context)
check('SSH push updates only the approved reference', result.state === 'completed' && hostGit('--git-dir=' + remote, 'rev-parse', 'ssh-pushed').trim().length === 40)
const badPin = await importGitCredential(sshUrl, { type: 'ssh', privateKey: clientPem, knownHosts: `[127.0.0.1]:${sshPort} ssh-rsa ${clientPublic.toString('base64')}\n` })
result = await gitRemoteAction({ action: 'ls-remote', url: sshUrl, credentialId: badPin.id }, context)
check('a mismatched SSH host key fails closed', result.state === 'failed' && /host key|identification/i.test(result.reason ?? ''))
const cloneWorkspace = join(fixture, 'clone-project'), cloneContext = { ...context, workspaceRoot: cloneWorkspace, sessionId: 'clone-session' }
await fs.mkdir(cloneWorkspace)
const cloned = await executeTool('sandbox_git', { action: 'clone', url: httpUrl }, cloneContext)
await fs.writeFile(join(fixture, 'clone-result.json'), JSON.stringify(cloned, null, 2))
check('clone fetches and checks out the default branch inside an empty draft', cloned.ok && !await fs.lstat(join(cloneWorkspace, 'file.txt')).catch(() => null))
check('cloned worktree bytes are readable through isolated Git commands', (await executeTool('sandbox_git', { action: 'run', argv: ['show', 'HEAD:file.txt'] }, cloneContext)).content.includes('remote-base'))
check('ordinary clone fetches the other remote branches as well as the default branch', (await executeTool('sandbox_git', { action: 'run', argv: ['branch', '-r'] }, cloneContext)).content.includes('origin/ssh-pushed'))
const clonePublished = await workspaceAction('commit', cloneContext)
check('clone publication creates a real host repository with its original remote ancestry', clonePublished.writeback === 'committed' && !!clonePublished.git?.groupId && (await fs.readFile(join(cloneWorkspace, 'file.txt'), 'utf8')).includes('remote-base'))
const maintenanceBundle = join(fixture, 'maintenance.mjs'), cliProject = join(fixture, 'cli-project'), cliRequest = join(fixture, 'cli-request.json')
await require('esbuild').build({ entryPoints: ['scripts/sandbox-maintain.ts'], bundle: true, platform: 'node', format: 'esm', outfile: maintenanceBundle, logLevel: 'silent' })
await fs.mkdir(cliProject)
const cli = (command: string): Promise<any> => new Promise((done, reject) => {
const child = spawn(process.execPath, [maintenanceBundle, command, '--workspace', cliProject, '--session', 'cli-session', '--state-root', stateRoot, '--request', cliRequest, '--approve'], { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] })
let stdout = '', stderr = ''; const timer = setTimeout(() => child.kill(), 120000)
child.stdout.on('data', data => { stdout += data }); child.stderr.on('data', data => { stderr += data })
child.once('error', reject); child.once('close', code => { clearTimeout(timer); if (code) reject(new Error(`Git maintenance failed (${code}): ${stderr}`)); else { try { done(JSON.parse(stdout)) } catch (error) { reject(error) } } })
})
await fs.writeFile(cliRequest, JSON.stringify({ action: 'clone', url: httpUrl }))
check('maintenance clone performs the same isolated checkout as the conversation tool', (await cli('git-remote')).ok)
await fs.writeFile(cliRequest, JSON.stringify(['show', 'HEAD:file.txt']))
const cliRead = await cli('git-run')
check('a fresh maintenance process sees the cloned history and retained draft', cliRead.ok && cliRead.content.includes('remote-base'))
const emptyRemote = join(fixture, 'empty.git'), emptyProject = join(fixture, 'empty-project')
hostGit('init', '--bare', '-q', '--template=', '--initial-branch=main', emptyRemote); remotePaths.set('/empty.git', emptyRemote); await fs.mkdir(emptyProject)
const emptyClone = await executeTool('sandbox_git', { action: 'clone', url: `http://127.0.0.1:${httpPort}/empty.git` }, { ...context, workspaceRoot: emptyProject, sessionId: 'empty-clone' })
check('cloning an empty remote retains an unborn private repository', emptyClone.ok && !await fs.lstat(join(emptyProject, '.git')).catch(() => null))
const shaRemote = join(fixture, 'sha256.git'), shaProject = join(fixture, 'sha256-project')
hostGit('init', '--bare', '-q', '--object-format=sha256', '--template=', '--initial-branch=main', shaRemote)
const shaTree = hostGit('--git-dir=' + shaRemote, 'hash-object', '-w', '-t', 'tree', '--stdin').trim(), shaCommit = hostGit('--git-dir=' + shaRemote, 'commit-tree', shaTree, '-m', 'sha256-history').trim()
hostGit('--git-dir=' + shaRemote, 'update-ref', 'refs/heads/main', shaCommit); remotePaths.set('/sha256.git', shaRemote); await fs.mkdir(shaProject)
const shaContext = { ...context, workspaceRoot: shaProject, sessionId: 'sha256-clone' }
const shaClone = await executeTool('sandbox_git', { action: 'clone', url: `http://127.0.0.1:${httpPort}/sha256.git` }, shaContext)
check('clone detects and preserves a real SHA-256 repository', shaClone.ok && shaCommit.length === 64 && (await executeTool('sandbox_git', { action: 'run', argv: ['rev-parse', 'HEAD'] }, shaContext)).content.includes(shaCommit))
await removeGitCredential(credential.id)
check('credential removal revokes subsequent lookup', !(await listGitCredentials()).some(value => value.id === credential.id))
for (const bad of ['ext::echo pwned', 'file:///C:/private.git', 'https://token@example.invalid/repo.git', 'http://example.invalid/repo.git', 'ssh://git@host/repo;bad']) assert.throws(() => gitRemoteUrl(bad))
check('remote URL parsing rejects helper execution, inline secrets and unsafe protocols', true)
const scp = 'fixture@example.invalid:relative/repo.git'
check('SCP remotes retain relative transport paths while credentials use an unambiguous scope', gitRemoteUrl(scp).pathname === '/~/relative/repo.git' && gitRemoteEndpoint(scp, gitRemoteUrl(scp)) === scp)
disconnectPush = true
result = await gitRemoteAction({ action: 'push', url: httpUrl, refspecs: ['HEAD:refs/heads/lost-reply'] }, context)
check('lost push acknowledgement remains unknown after real remote refs changed', result.state === 'unknown' && result.writeback === 'unknown' && hostGit('--git-dir=' + remote, 'rev-parse', 'lost-reply').trim().length === 40)
check('durable query never infers lost push success or retries it', (await gitRemoteOperation(context, result.operationId)).state === 'unknown')
disconnectPush = false
const reconciled = await reconcileGitRemote(context, result.operationId)
check('explicit reconciliation confirms current remote refs without repeating the push', reconciled.state === 'completed' && reconciled.reason!.includes('observing the current remote refs'))
const heldOperation = async (cancel: () => Promise<void>, signal?: AbortSignal) => {
holdResponses = true
let timer: ReturnType<typeof setTimeout> | undefined
const requested = new Promise<void>((done, reject) => { onHeldRequest = done; timer = setTimeout(() => reject(new Error('Git did not reach the real transport cancellation boundary')), 15000) })
const operation = gitRemoteAction({ action: 'ls-remote', url: httpUrl }, { ...context, signal })
try { await requested; await cancel(); return await operation }
finally { clearTimeout(timer); holdResponses = false; onHeldRequest = undefined; for (const response of heldResponses) response.destroy() }
}
const controller = new AbortController()
const cancelled = await heldOperation(async () => controller.abort(), controller.signal)
check('cancelling an active real Git transport terminates its Job and saves the failed read outcome', cancelled.state === 'failed' && /cancel/i.test(cancelled.reason ?? '') && (await gitRemoteOperation(context, cancelled.operationId)).state === 'failed')
const revoked = await heldOperation(async () => { const policy = await readSandboxPolicy(); await updateSandboxPolicy({ expectedRevision: policy.revision, rules: { deniedPaths: ['revoked-by-test.txt'] } }) })
check('policy revocation terminates an in-flight trusted Git transport', revoked.state === 'failed' && /policy|策略/i.test(revoked.reason ?? ''))
console.log(`SANDBOX_GIT_REMOTE_PASS ${checks.length}/${checks.length}`)
} finally {
for (const child of children) child.kill()
for (const client of clients) client.end()
http.closeAllConnections(); https.closeAllConnections(); http.close(); https.close(); ssh.close()
await fs.writeFile(join(fixture, 'report.json'), JSON.stringify({ fixture, checks, approvals, httpRequests, sshConnections }, null, 2) + '\n')
console.log(`SANDBOX_GIT_REMOTE_REPORT ${join(fixture, 'report.json')}`)
}