feat: complete WorkBuddy parity and Windows release workflow
This commit is contained in:
1 parent
143516b3a8
commit
d5512df549
434 files changed
+54977
-1854
No files matched your search
@@ -0,0 +1,223 @@
|
||||
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')}`)
|
||||
}
|
||||
Reference in new issue
Block a user