import { describe, expect, it, vi } from 'vitest'; import type { CommandResult, CommandRunner, FleetRoster } from '../commands/fleet.js'; import { FleetTmuxRuntimeTransport } from './tmux-runtime-transport.js'; import type { FleetRuntimeTransportError } from './tmux-runtime-transport.js'; const roster: FleetRoster = { version: 1, transport: 'tmux', tmux: { socketName: 'tess-fleet', holderSession: '_holder' }, defaults: { workingDirectory: '~/src' }, runtimes: { codex: { resetCommand: '/clear' } }, agents: [{ name: 'coder0', runtime: 'codex', className: 'code' }], }; function commandResult(stdout = '', exitCode = 0, stderr = ''): CommandResult { return { stdout, stderr, exitCode }; } describe('FleetTmuxRuntimeTransport security boundary', (): void => { it('rejects a prefix target before it invokes tmux', async (): Promise => { const runner = vi.fn(async (): Promise => commandResult()); const transport = new FleetTmuxRuntimeTransport({ rosterLoader: async (): Promise => roster, runner, mosaicHome: '/mosaic', }); await expect(transport.verifySession('coder')).rejects.toMatchObject({ code: 'not_found', } satisfies Partial); expect(runner).not.toHaveBeenCalled(); }); it('uses the roster socket and exact pane target while verifying peer identity', async (): Promise => { const runner = vi.fn( async (): Promise => commandResult('111 codex 0 0 0 0\n'), ); const transport = new FleetTmuxRuntimeTransport({ rosterLoader: async (): Promise => roster, runner, mosaicHome: '/mosaic', }); await expect(transport.verifySession('coder0')).resolves.toEqual({ id: 'coder0', runtimeId: 'codex', socketName: 'tess-fleet', }); expect(runner).toHaveBeenCalledWith('tmux', [ '-L', 'tess-fleet', 'list-panes', '-t', '=coder0:0.0', '-F', '#{pane_pid} #{pane_current_command} #{pane_dead} #{pane_activity} #{window_activity} #{session_activity}', ]); }); it('denies a live pane whose runtime does not match the roster identity', async (): Promise => { const runner = vi.fn( async (): Promise => commandResult('111 python3 0 0 0 0\n'), ); const transport = new FleetTmuxRuntimeTransport({ rosterLoader: async (): Promise => roster, runner, mosaicHome: '/mosaic', }); await expect(transport.verifySession('coder0')).rejects.toMatchObject({ code: 'forbidden', } satisfies Partial); expect(runner).toHaveBeenCalledTimes(1); }); it('surfaces a runtime identity mismatch instead of hiding it from fleet status', async (): Promise => { const rosterWithMismatchedPeer: FleetRoster = { ...roster, agents: [...roster.agents, { name: 'coder1', runtime: 'codex', className: 'code' }], }; const runner = vi.fn( async (_command: string, args: string[]): Promise => commandResult( args.includes('=coder1:0.0') ? '222 python3 0 0 0 0\n' : '111 codex 0 0 0 0\n', ), ); const transport = new FleetTmuxRuntimeTransport({ rosterLoader: async (): Promise => rosterWithMismatchedPeer, runner, mosaicHome: '/mosaic', }); await expect(transport.listSessions()).rejects.toMatchObject({ code: 'forbidden', } satisfies Partial); }); it('denies sending when the roster socket does not contain the exact target', async (): Promise => { const runner = vi.fn( async (): Promise => commandResult('', 1, "can't find session: coder0"), ); const transport = new FleetTmuxRuntimeTransport({ rosterLoader: async (): Promise => roster, runner, mosaicHome: '/mosaic', }); await expect(transport.sendMessage('coder0', 'hello', 'tess')).rejects.toMatchObject({ code: 'unavailable', } satisfies Partial); expect(runner).toHaveBeenCalledTimes(1); }); it('sends only through the maintained sender after exact target and identity verification', async (): Promise => { const runner = vi .fn() .mockResolvedValueOnce(commandResult('111 codex 0 0 0 0\n')) .mockResolvedValueOnce(commandResult()); const transport = new FleetTmuxRuntimeTransport({ rosterLoader: async (): Promise => roster, runner, mosaicHome: '/mosaic', }); await transport.sendMessage('coder0', 'hello', 'tess'); expect(runner).toHaveBeenNthCalledWith(2, '/mosaic/tools/tmux/agent-send.sh', [ '-L', 'tess-fleet', '-S', 'tess', '-s', 'coder0', '-m', 'hello', ]); }); it('terminates only the exact roster target after identity verification', async (): Promise => { const runner = vi .fn() .mockResolvedValueOnce(commandResult('111 codex 0 0 0 0\n')) .mockResolvedValueOnce(commandResult()); const transport = new FleetTmuxRuntimeTransport({ rosterLoader: async (): Promise => roster, runner, mosaicHome: '/mosaic', }); await transport.terminate('coder0'); expect(runner).toHaveBeenNthCalledWith(2, 'tmux', [ '-L', 'tess-fleet', 'kill-session', '-t', '=coder0', ]); }); });