import { spawn } from 'node:child_process' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { Server, utils as sshUtils } from 'ssh2' import { ReceivePackSession } from '../../server/utils/git-wire/receive-pack' import { ssh2ReceivePackFactory } from '../../server/utils/git-wire/receive-pack' import { ZERO_SHA } from '../../server/utils/git-wire/refs' import { generateKeypair, pkcs8ToOpenSshPrivate } from '../../server/utils/ssh-keypair' import { fakeGithubFetch, GitFixture } from '../utils/git-wire' import { fetchPack } from '../../server/utils/git-wire/upload-pack' async function* fromBuffer(b: Buffer): AsyncGenerator { yield b } async function drain(gen: AsyncGenerator): Promise { const parts: Buffer[] = [] for await (const c of gen) parts.push(c) return Buffer.concat(parts) } /** * An in-process ssh2 server that authorises one public key and runs the real * `git-receive-pack` against the given bare repo on exec. Mirrors the knot's * `git@host: git-receive-pack ''` surface so the ssh2 factory is exercised * end to end. */ function startKnotServer(authorizedPubKey: string, repoFor: (path: string) => string | null) { const hostKey = sshUtils.generateKeyPairSync('ed25519').private const parsed = sshUtils.parseKey(authorizedPubKey) if (parsed instanceof Error) throw parsed const allowed = Array.isArray(parsed) ? parsed[0]! : parsed const allowedSSH = allowed.getPublicSSH() const server = new Server({ hostKeys: [hostKey] }, client => { client.on('authentication', ctx => { if (ctx.method === 'publickey' && ctx.key.algo === allowed.type && ctx.key.data.equals(allowedSSH)) { ctx.accept() return } ctx.reject() }) client.on('ready', () => { client.on('session', accept => { accept().once('exec', (acceptExec, _reject, info) => { const match = info.command.match(/^git-receive-pack '(.+)'$/) const repo = match ? repoFor(match[1]!) : null const stream = acceptExec() if (!repo) { stream.stderr.write('repository not found\n') stream.exit(128) stream.end() return } const child = spawn('git-receive-pack', [repo], { stdio: ['pipe', 'pipe', 'pipe'] }) stream.pipe(child.stdin) child.stdout.pipe(stream) child.stderr.on('data', (d: Buffer) => stream.stderr.write(d)) child.on('close', code => { stream.exit(code ?? 0); stream.end() }) }) }) }) }) return new Promise<{ port: number, close: () => void }>(resolve => { server.listen(0, '127.0.0.1', () => { resolve({ port: (server.address() as { port: number }).port, close: () => server.close() }) }) }) } describe('ssh2ReceivePackFactory (against an in-process ssh2 knot)', () => { let fx: GitFixture let realFetch: typeof globalThis.fetch let knotServer: { port: number, close: () => void } | null = null beforeEach(() => { fx = new GitFixture() realFetch = globalThis.fetch }) afterEach(() => { globalThis.fetch = realFetch knotServer?.close() knotServer = null fx.cleanup() }) async function packFor(ghBare: string, want: string, haves: string[]): Promise { globalThis.fetch = fakeGithubFetch(new Map([['owner/repo', ghBare]])) as unknown as typeof globalThis.fetch const { pack } = await fetchPack({ repoFullName: 'owner/repo', token: 't', want, haves, maxBytes: 1 << 30 }) return drain(pack) } it('pushes a ref to the knot over a real ssh2 connection', async () => { const gh = fx.initBare('gh.git') const work = fx.initWork('work') const sha = fx.commit(work, 'a.txt', 'hello') fx.pushTo(work, gh, 'HEAD:refs/heads/main') const knot = fx.initBare('knot.git') const key = generateKeypair('synchub.to/1') knotServer = await startKnotServer(key.publicKeyOpenSsh, p => (p === '/repo-did' ? knot : null)) const factory = ssh2ReceivePackFactory({ host: '127.0.0.1', port: knotServer.port, repoPath: '/repo-did', // Mirror production: the worker hands ssh2 the OpenSSH-format key that // `loadSshKeyForInstall` derives from the stored PKCS#8 PEM. privateKey: pkcs8ToOpenSshPrivate(key.privateKeyPem, 'synchub.to/1'), }) const session = await ReceivePackSession.open(factory) await session.push([{ ref: 'refs/heads/main', old: ZERO_SHA, next: sha }], fromBuffer(await packFor(gh, sha, []))) expect(fx.revParse(knot, 'refs/heads/main')).toBe(sha) }) it('surfaces a connection failure as a caught WireError, not an uncaught throw', async () => { const key = generateKeypair('synchub.to/1') // Nothing listening on this port: connect() emits 'error' (ECONNREFUSED). const factory = ssh2ReceivePackFactory({ host: '127.0.0.1', port: 1, repoPath: '/repo-did', privateKey: pkcs8ToOpenSshPrivate(key.privateKeyPem, 'synchub.to/1'), }) await expect(ReceivePackSession.open(factory)).rejects.toThrow(/ssh error|ECONNREFUSED|advertisement|end of stream/) }) })